[gcc r12-7164] nvptx: Expand QI mode operations using SI mode instructions

Tom de Vries vries@gcc.gnu.org
Thu Feb 10 08:02:27 GMT 2022


https://gcc.gnu.org/g:de12b919c74307c5c2a4c79a29683d21e622422e

commit r12-7164-gde12b919c74307c5c2a4c79a29683d21e622422e
Author: Roger Sayle <roger@nextmovesoftware.com>
Date:   Thu Feb 3 09:21:58 2022 +0100

    nvptx: Expand QI mode operations using SI mode instructions
    
    One of the unusual target features of the Nvidia PTX ISA is that it
    doesn't provide QI mode (byte sized) operations or registers.  Somewhat
    conventionally, 8-bit quantities are read from/written to memory using
    special instructions, but stored internally using SImode (32-bit) registers.
    GCC's middle-end accomodates targets without QImode optabs, by widening
    operations until suitable support is found, and with the current nvptx
    backend this means 16-bit HImode operations.  The inconvenience is that
    nvptx is also a TARGET_TRULY_NOOP_TRUNCATION=false target, meaning that
    additional instructions are required to convert between the SImode
    registers used to hold QImode values, and the HImode registers used to
    operate on them (and back again).  This results in a large amount of
    shuffling and type conversion in code dealing with bytes, i.e. using
    char or Boolean types.
    
    This patch improves the situation by providing expanders in the nvptx
    machine description to perform QImode operations natively in SImode
    instead of HImode.  An alternate implementation might be to provide
    some form of target hook to specify which fallback modes to use during
    RTL expansion, but I think this requirement is unusual, and a solution
    entirely in the nvptx backend doesn't disturb/affect other targets.
    
    The improvements can be quite dramatic, as shown in the example below:
    
    int foo(int x, int y) { return (x==21) && (y==69); }
    
    previously with -O2 required 15 instructions:
    
                    mov.u32 %r26, %ar0;
                    mov.u32 %r27, %ar1;
                    setp.eq.u32     %r31, %r26, 21;
                    selp.u32        %r30, 1, 0, %r31;
                    mov.u32 %r29, %r30;
                    setp.eq.u32     %r34, %r27, 69;
                    selp.u32        %r33, 1, 0, %r34;
                    mov.u32 %r32, %r33;
                    cvt.u16.u8      %r39, %r29;
                    mov.u16 %r36, %r39;
                    cvt.u16.u8      %r39, %r32;
                    mov.u16 %r37, %r39;
                    and.b16 %r35, %r36, %r37;
                    cvt.u32.u16     %r38, %r35;
                    cvt.u32.u8      %value, %r38;
    
    with this patch, now requires only 7 instructions:
    
                    mov.u32 %r26, %ar0;
                    mov.u32 %r27, %ar1;
                    setp.eq.u32     %r31, %r26, 21;
                    setp.eq.u32     %r34, %r27, 69;
                    selp.u32        %r37, 1, 0, %r31;
                    selp.u32        %r38, 1, 0, %r34;
                    and.b32 %value, %r37, %r38;
    
    This patch has been tested on nvptx-none hosted on x86_64-pc-linux-gnu
    (including newlib) with a make and make -k check with no new failures.
    
    gcc/ChangeLog:
    
            * config/nvptx/nvptx.md (cmp<mode>): Renamed from *cmp<mode>.
            (setcc<mode>_from_bi): Additionally support QImode.
            (extendbi<mode>2): Additionally support QImode.
            (zero_extendbi<mode>2): Additionally support QImode.
            (any_sbinary, any_ubinary, any_sunary, any_uunary): New code
            iterators for signed and unsigned, binary and unary operations.
            (<sbinary>qi3, <ubinary>qi3, <sunary>qi2, <uunary>qi2): New
            expanders to perform QImode operations using SImode instructions.
            (cstoreqi4): New define_expand.
            (*ext_truncsi2_qi): New define_insn.
            (*zext_truncsi2_qi): New define_insn.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/nvptx/bool-1.c: New test case.

Diff:
---
 gcc/config/nvptx/nvptx.md               | 114 ++++++++++++++++++++++++++++++--
 gcc/testsuite/gcc.target/nvptx/bool-1.c |  16 +++++
 2 files changed, 123 insertions(+), 7 deletions(-)

diff --git a/gcc/config/nvptx/nvptx.md b/gcc/config/nvptx/nvptx.md
index e26d24ed650..f53809eacf5 100644
--- a/gcc/config/nvptx/nvptx.md
+++ b/gcc/config/nvptx/nvptx.md
@@ -767,7 +767,7 @@
 
 ;; Comparisons and branches
 
-(define_insn "*cmp<mode>"
+(define_insn "cmp<mode>"
   [(set (match_operand:BI 0 "nvptx_register_operand" "=R")
 	(match_operator:BI 1 "nvptx_comparison_operator"
 	   [(match_operand:HSDIM 2 "nvptx_register_operand" "R")
@@ -879,22 +879,22 @@
 ;; Conditional stores
 
 (define_insn "setcc<mode>_from_bi"
-  [(set (match_operand:HSDIM 0 "nvptx_register_operand" "=R")
-	(ne:HSDIM (match_operand:BI 1 "nvptx_register_operand" "R")
+  [(set (match_operand:QHSDIM 0 "nvptx_register_operand" "=R")
+	(ne:QHSDIM (match_operand:BI 1 "nvptx_register_operand" "R")
 		   (const_int 0)))]
   ""
   "%.\\tselp%t0\\t%0, 1, 0, %1;")
 
 (define_insn "extendbi<mode>2"
-  [(set (match_operand:HSDIM 0 "nvptx_register_operand" "=R")
-	(sign_extend:HSDIM
+  [(set (match_operand:QHSDIM 0 "nvptx_register_operand" "=R")
+	(sign_extend:QHSDIM
 	 (match_operand:BI 1 "nvptx_register_operand" "R")))]
   ""
   "%.\\tselp%t0\\t%0, -1, 0, %1;")
 
 (define_insn "zero_extendbi<mode>2"
-  [(set (match_operand:HSDIM 0 "nvptx_register_operand" "=R")
-	(zero_extend:HSDIM
+  [(set (match_operand:QHSDIM 0 "nvptx_register_operand" "=R")
+	(zero_extend:QHSDIM
 	 (match_operand:BI 1 "nvptx_register_operand" "R")))]
   ""
   "%.\\tselp%t0\\t%0, 1, 0, %1;")
@@ -2117,3 +2117,103 @@
     return nvptx_output_red_partition (operands[0], operands[1]);
   }
   [(set_attr "predicable" "false")])
+
+;; Expand QI mode operations using SI mode instructions.
+(define_code_iterator any_sbinary [plus minus smin smax])
+(define_code_attr sbinary [(plus "add") (minus "sub") (smin "smin") (smax "smax")])
+
+(define_code_iterator any_ubinary [and ior xor umin umax])
+(define_code_attr ubinary [(and "and") (ior "ior") (xor "xor") (umin "umin")
+			   (umax "umax")])
+
+(define_code_iterator any_sunary [neg abs])
+(define_code_attr sunary [(neg "neg") (abs "abs")])
+
+(define_code_iterator any_uunary [not])
+(define_code_attr uunary [(not "one_cmpl")])
+
+(define_expand "<sbinary>qi3"
+  [(set (match_operand:QI 0 "nvptx_register_operand")
+	(any_sbinary:QI (match_operand:QI 1 "nvptx_nonmemory_operand")
+			(match_operand:QI 2 "nvptx_nonmemory_operand")))]
+  ""
+{
+  rtx reg = gen_reg_rtx (SImode);
+  rtx op0 = convert_modes (SImode, QImode, operands[1], 0);
+  rtx op1 = convert_modes (SImode, QImode, operands[2], 0);
+  if (<CODE> == MINUS)
+    op0 = force_reg (SImode, op0);
+  emit_insn (gen_<sbinary>si3 (reg, op0, op1));
+  emit_insn (gen_truncsiqi2 (operands[0], reg));
+  DONE;
+})
+
+(define_expand "<ubinary>qi3"
+  [(set (match_operand:QI 0 "nvptx_register_operand")
+	(any_ubinary:QI (match_operand:QI 1 "nvptx_nonmemory_operand")
+			(match_operand:QI 2 "nvptx_nonmemory_operand")))]
+  ""
+{
+  rtx reg = gen_reg_rtx (SImode);
+  rtx op0 = convert_modes (SImode, QImode, operands[1], 1);
+  rtx op1 = convert_modes (SImode, QImode, operands[2], 1);
+  emit_insn (gen_<ubinary>si3 (reg, op0, op1));
+  emit_insn (gen_truncsiqi2 (operands[0], reg));
+  DONE;
+})
+
+(define_expand "<sunary>qi2"
+  [(set (match_operand:QI 0 "nvptx_register_operand")
+	(any_sunary:QI (match_operand:QI 1 "nvptx_nonmemory_operand")))]
+  ""
+{
+  rtx reg = gen_reg_rtx (SImode);
+  rtx op0 = convert_modes (SImode, QImode, operands[1], 0);
+  emit_insn (gen_<sunary>si2 (reg, op0));
+  emit_insn (gen_truncsiqi2 (operands[0], reg));
+  DONE;
+})
+
+(define_expand "<uunary>qi2"
+  [(set (match_operand:QI 0 "nvptx_register_operand")
+	(any_uunary:QI (match_operand:QI 1 "nvptx_nonmemory_operand")))]
+  ""
+{
+  rtx reg = gen_reg_rtx (SImode);
+  rtx op0 = convert_modes (SImode, QImode, operands[1], 1);
+  emit_insn (gen_<uunary>si2 (reg, op0));
+  emit_insn (gen_truncsiqi2 (operands[0], reg));
+  DONE;
+})
+
+(define_expand "cstoreqi4"
+  [(set (match_operand:SI 0 "nvptx_register_operand")
+	(match_operator:SI 1 "nvptx_comparison_operator"
+	  [(match_operand:QI 2 "nvptx_nonmemory_operand")
+	   (match_operand:QI 3 "nvptx_nonmemory_operand")]))]
+  ""
+{
+  rtx reg = gen_reg_rtx (BImode);
+  enum rtx_code code = GET_CODE (operands[1]);
+  int unsignedp = unsigned_condition_p (code);
+  rtx op2 = convert_modes (SImode, QImode, operands[2], unsignedp);
+  rtx op3 = convert_modes (SImode, QImode, operands[3], unsignedp);
+  rtx cmp = gen_rtx_fmt_ee (code, SImode, op2, op3);
+  emit_insn (gen_cmpsi (reg, cmp, op2, op3));
+  emit_insn (gen_setccsi_from_bi (operands[0], reg));
+  DONE;
+})
+
+(define_insn "*ext_truncsi2_qi"
+  [(set (match_operand:SI 0 "nvptx_register_operand" "=R")
+	(sign_extend:SI
+	 (truncate:QI (match_operand:SI 1 "nvptx_register_operand" "R"))))]
+  ""
+  "%.\\tcvt.s32.s8\\t%0, %1;")
+
+(define_insn "*zext_truncsi2_qi"
+  [(set (match_operand:SI 0 "nvptx_register_operand" "=R")
+	(zero_extend:SI
+	 (truncate:QI (match_operand:SI 1 "nvptx_register_operand" "R"))))]
+  ""
+  "%.\\tcvt.u32.u8\\t%0, %1;")
diff --git a/gcc/testsuite/gcc.target/nvptx/bool-1.c b/gcc/testsuite/gcc.target/nvptx/bool-1.c
new file mode 100644
index 00000000000..58df2b014ea
--- /dev/null
+++ b/gcc/testsuite/gcc.target/nvptx/bool-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int
+foo (int x, int y)
+{
+  return (x == 21) && (y == 69);
+}
+
+/* { dg-final { scan-assembler-not "cvt.u16.u8" } } */
+/* { dg-final { scan-assembler-not "cvt.u32.u16" } } */
+/* { dg-final { scan-assembler-not "cvt.u32.u8" } } */
+
+/* { dg-final { scan-assembler-times "setp.eq.u32" 2 } } */
+/* { dg-final { scan-assembler-times "selp.u32" 2 } } */
+/* { dg-final { scan-assembler-times "and.b32" 1 } } */


More information about the Gcc-cvs mailing list