]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
s390: implement flags output
authorJuergen Christ <jchrist@linux.ibm.com>
Mon, 20 Nov 2023 08:13:10 +0000 (09:13 +0100)
committerAndreas Krebbel <krebbel@linux.ibm.com>
Thu, 23 Nov 2023 14:32:17 +0000 (15:32 +0100)
Implement flags output for inline assemblies.  Only use one output constraint
that captures the whole condition code.  No breakout into different condition
codes is allowed.  Also, only one condition code variable is allowed.

Add further logic to canonicalize various cases where we combine different
cases of possible condition codes.

gcc/ChangeLog:

* config/s390/s390-c.cc (s390_cpu_cpp_builtins): Define
__GCC_ASM_FLAG_OUTPUTS__.
* config/s390/s390.cc (s390_canonicalize_comparison): More
UNSPEC_CC_TO_INT cases.
(s390_md_asm_adjust): Implement flags output.
* config/s390/s390.md (ccstore4): Allow mask operands.
* doc/extend.texi: Document flags output.

gcc/testsuite/ChangeLog:

* gcc.target/s390/ccor.c: New test.

Signed-off-by: Juergen Christ <jchrist@linux.ibm.com>
gcc/config/s390/s390-c.cc
gcc/config/s390/s390.cc
gcc/config/s390/s390.md
gcc/doc/extend.texi
gcc/testsuite/gcc.target/s390/ccor.c [new file with mode: 0644]

index fce569342f30f2815cdcc1e518205a5b87920a13..7e455abf387ea4a77c5a9703b66e003790f0b5a8 100644 (file)
@@ -409,6 +409,7 @@ s390_cpu_cpp_builtins (cpp_reader *pfile)
     cpp_define (pfile, "__LONG_DOUBLE_128__");
   cl_target_option_save (&opts, &global_options, &global_options_set);
   s390_cpu_cpp_builtins_internal (pfile, &opts, NULL);
+  cpp_define (pfile, "__GCC_ASM_FLAG_OUTPUTS__");
 }
 
 #if S390_USE_TARGET_ATTRIBUTE
index e36efec8ddcbe3cba7b10f2e75827d497f643643..b3603bea7a45b5e97167522a54d9298156f4d716 100644 (file)
@@ -1874,6 +1874,97 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
          *code = new_code;
        }
     }
+  /* Remove UNSPEC_CC_TO_INT from connectives.  This happens for
+     checks against multiple condition codes. */
+  if (GET_CODE (*op0) == AND
+      && GET_CODE (XEXP (*op0, 0)) == UNSPEC
+      && XINT (XEXP (*op0, 0), 1) == UNSPEC_CC_TO_INT
+      && XVECLEN (XEXP (*op0, 0), 0) == 1
+      && REGNO (XVECEXP (XEXP (*op0, 0), 0, 0)) == CC_REGNUM
+      && CONST_INT_P (XEXP (*op0, 1))
+      && CONST_INT_P (*op1)
+      && INTVAL (XEXP (*op0, 1)) == -3
+      && *code == EQ)
+    {
+      if (INTVAL (*op1) == 0)
+       {
+         /* case cc == 0 || cc = 2 => mask = 0xa */
+         *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+         *op1 = gen_rtx_CONST_INT (VOIDmode, 0xa);
+       }
+      else if (INTVAL (*op1) == 1)
+       {
+         /* case cc == 1 || cc == 3 => mask = 0x5 */
+         *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+         *op1 = gen_rtx_CONST_INT (VOIDmode, 0x5);
+       }
+    }
+  if (GET_CODE (*op0) == PLUS
+      && GET_CODE (XEXP (*op0, 0)) == UNSPEC
+      && XINT (XEXP (*op0, 0), 1) == UNSPEC_CC_TO_INT
+      && XVECLEN (XEXP (*op0, 0), 0) == 1
+      && REGNO (XVECEXP (XEXP (*op0, 0), 0, 0)) == CC_REGNUM
+      && CONST_INT_P (XEXP (*op0, 1))
+      && CONST_INT_P (*op1)
+      && (*code == LEU || *code == GTU))
+    {
+      if (INTVAL (*op1) == 1)
+       {
+         if (INTVAL (XEXP (*op0, 1)) == -1)
+           {
+             /* case cc == 1 || cc == 2 => mask = 0x6 */
+             *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+             *op1 = gen_rtx_CONST_INT (VOIDmode, 0x6);
+             *code = *code == GTU ? NE : EQ;
+           }
+         else if (INTVAL (XEXP (*op0, 1)) == -2)
+           {
+             /* case cc == 2 || cc == 3 => mask = 0x3 */
+             *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+             *op1 = gen_rtx_CONST_INT (VOIDmode, 0x3);
+             *code = *code == GTU ? NE : EQ;
+           }
+       }
+      else if (INTVAL (*op1) == 2
+              && INTVAL (XEXP (*op0, 1)) == -1)
+       {
+         /* case cc == 1 || cc == 2 || cc == 3 => mask = 0x7 */
+         *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+         *op1 = gen_rtx_CONST_INT (VOIDmode, 0x7);
+         *code = *code == GTU ? NE : EQ;
+       }
+    }
+  else if (*code == LEU || *code == GTU)
+    {
+      if (GET_CODE (*op0) == UNSPEC
+         && XINT (*op0, 1) == UNSPEC_CC_TO_INT
+         && XVECLEN (*op0, 0) == 1
+         && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+         && CONST_INT_P (*op1))
+       {
+         if (INTVAL (*op1) == 1)
+           {
+             /* case cc == 0 || cc == 1 => mask = 0xc */
+             *op0 = XVECEXP (*op0, 0, 0);
+             *op1 = gen_rtx_CONST_INT (VOIDmode, 0xc);
+             *code = *code == GTU ? NE : EQ;
+           }
+         else if (INTVAL (*op1) == 2)
+           {
+             /* case cc == 0 || cc == 1 || cc == 2 => mask = 0xd */
+             *op0 = XVECEXP (*op0, 0, 0);
+             *op1 = gen_rtx_CONST_INT (VOIDmode, 0xd);
+             *code = *code == GTU ? NE : EQ;
+           }
+         else if (INTVAL (*op1) == 3)
+           {
+             /* always true */
+             *op0 = const0_rtx;
+             *op1 = const0_rtx;
+             *code = *code == GTU ? NE : EQ;
+           }
+       }
+    }
 
   /* Simplify cascaded EQ, NE with const0_rtx.  */
   if ((*code == NE || *code == EQ)
@@ -17425,22 +17516,60 @@ static rtx_insn *
 s390_md_asm_adjust (vec<rtx> &outputs, vec<rtx> &inputs,
                    vec<machine_mode> &input_modes,
                    vec<const char *> &constraints, vec<rtx> & /*clobbers*/,
-                   HARD_REG_SET & /*clobbered_regs*/, location_t /*loc*/)
+                   HARD_REG_SET &clobbered_regs, location_t loc)
 {
-  if (!TARGET_VXE)
-    /* Long doubles are stored in FPR pairs - nothing to do.  */
-    return NULL;
 
   rtx_insn *after_md_seq = NULL, *after_md_end = NULL;
+  bool saw_cc = false;
 
   unsigned ninputs = inputs.length ();
   unsigned noutputs = outputs.length ();
   for (unsigned i = 0; i < noutputs; i++)
     {
+      const char *constraint = constraints[i];
+      if (strncmp (constraint, "=@cc", 4) == 0)
+       {
+         if (constraint[4] != 0)
+           {
+             error_at (loc, "invalid cc output constraint: %qs", constraint);
+             continue;
+           }
+         if (saw_cc)
+           {
+             error_at (loc, "multiple cc output constraints not supported");
+             continue;
+           }
+         if (TEST_HARD_REG_BIT (clobbered_regs, CC_REGNUM))
+           {
+             error_at (loc, "%<asm%> specifier for cc output conflicts with %<asm%> clobber list");
+             continue;
+           }
+         rtx dest = outputs[i];
+         if (GET_MODE (dest) != SImode)
+           {
+             error ("invalid type for cc output constraint");
+             continue;
+           }
+         saw_cc = true;
+         constraints[i] = "=c";
+         outputs[i] = gen_rtx_REG (CCRAWmode, CC_REGNUM);
+
+         push_to_sequence2 (after_md_seq, after_md_end);
+         emit_insn (gen_rtx_SET (dest,
+                                 gen_rtx_UNSPEC (SImode,
+                                                 gen_rtvec (1, outputs[i]),
+                                                 UNSPEC_CC_TO_INT)));
+         after_md_seq = get_insns ();
+         after_md_end = get_last_insn ();
+         end_sequence ();
+         continue;
+       }
+      if (!TARGET_VXE)
+       /* Long doubles are stored in FPR pairs - nothing to do.  */
+       continue;
       if (GET_MODE (outputs[i]) != TFmode)
        /* Not a long double - nothing to do.  */
        continue;
-      const char *constraint = constraints[i];
       bool allows_mem, allows_reg, is_inout;
       bool ok = parse_output_constraint (&constraint, i, ninputs, noutputs,
                                         &allows_mem, &allows_reg, &is_inout);
index 4aa4a94d994a03f7ebbac72c1ed4c8b20bdfeb48..e30795a00ab1706dd0850605957d718574257e74 100644 (file)
     [(set (match_operand:SI 0 "register_operand" "")
          (match_operator:SI 1 "s390_eqne_operator"
            [(match_operand 2 "cc_reg_operand")
-           (match_operand 3 "const0_operand")]))
+           (match_operand 3 "const_mask_operand")]))
      (clobber (reg:CC CC_REGNUM))])]
   ""
   "machine_mode mode = GET_MODE (operands[2]);
        rtx cond, ite;
 
        if (GET_CODE (operands[1]) == NE)
-        cond = gen_rtx_NE (VOIDmode, operands[2], const0_rtx);
+        cond = gen_rtx_NE (VOIDmode, operands[2], operands[3]);
        else
-        cond = gen_rtx_EQ (VOIDmode, operands[2], const0_rtx);
+        cond = gen_rtx_EQ (VOIDmode, operands[2], operands[3]);
        ite = gen_rtx_IF_THEN_ELSE (SImode, cond, const1_rtx, const0_rtx);
        emit_insn (gen_rtx_SET (operands[0], ite));
      }
    else
      {
-       if (mode != CCZ1mode)
+       if (mode != CCZ1mode || operands[3] != const0_rtx)
         FAIL;
        emit_insn (gen_sne (operands[0], operands[2]));
        if (GET_CODE (operands[1]) == EQ)
index 6dbe2a9125748e20fe4ba60ff38967fb36be7e13..1ae589aeb294611eebff1cb890002df712f98383 100644 (file)
@@ -10929,6 +10929,11 @@ sign flag set
 ``not'' @var{flag}, or inverted versions of those above
 @end table
 
+@item s390
+The flag output constraint for s390 is @samp{=@@cc}.  Only one such
+constraint is allowed.  The variable has to be stored in a @samp{int}
+variable.
+
 @end table
 
 @anchor{InputOperands}
diff --git a/gcc/testsuite/gcc.target/s390/ccor.c b/gcc/testsuite/gcc.target/s390/ccor.c
new file mode 100644 (file)
index 0000000..31f30f6
--- /dev/null
@@ -0,0 +1,88 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=zEC12 -mzarch" } */
+
+#define GENFUN1(C)                              \
+  int foo_ ##C(int x)                           \
+  {                                             \
+    int cc;                                    \
+    asm volatile ("ahi %[x],42\n"              \
+                 : [x] "+d"(x), "=@cc" (cc));  \
+    return cc == C ? 42 : 0;                   \
+  }
+
+#define GENFUN2(C1,C2)                          \
+  int foo_ ##C1##C2(int x)                      \
+  {                                             \
+    int cc;                                    \
+    asm volatile ("ahi %[x],42\n"              \
+                 : [x] "+d"(x), "=@cc" (cc));  \
+    return cc == C1 || cc == C2 ? 42 : 0;      \
+  }
+
+#define GENFUN3(C1,C2,C3)                                               \
+  int foo_ ##C1##C2##C3(int x)                                          \
+  {                                                                     \
+    int cc;                                                             \
+    asm volatile ("ahi %[x],42\n"                                      \
+                 : [x] "+d"(x), "=@cc" (cc));                          \
+    return cc == C1 || cc == C2 || cc == C3 ? 42 : 0;                  \
+  }
+
+GENFUN1(0)
+
+/* { dg-final { scan-assembler {locrne} } } */
+
+GENFUN1(1)
+
+/* { dg-final { scan-assembler {locrnl} } } */
+
+GENFUN1(2)
+
+/* { dg-final { scan-assembler {locrnh} } } */
+
+GENFUN1(3)
+
+/* { dg-final { scan-assembler {locrno} } } */
+
+GENFUN2(0,1)
+
+/* { dg-final { scan-assembler {locrnle} } } */
+
+GENFUN2(0,2)
+
+/* { dg-final { scan-assembler {locrhe} } } */
+
+GENFUN2(0,3)
+
+/* currently unoptimized */
+
+GENFUN2(1,2)
+
+/* { dg-final { scan-assembler {locrnlh} } } */
+
+GENFUN2(1,3)
+
+/* { dg-final { scan-assembler {locrnhe} } } */
+
+GENFUN2(2,3)
+
+/* { dg-final { scan-assembler {locrle} } } */
+
+GENFUN3(0,1,2)
+
+/* { dg-final { scan-assembler {locrh} } } */
+
+GENFUN3(0,1,3)
+
+/* currently unoptimized */
+
+GENFUN3(0,2,3)
+
+/* currently unoptimized */
+
+GENFUN3(1,2,3)
+
+/* { dg-final { scan-assembler {locre} } } */
+
+/* for the unoptimized cases, we get an ipm */
+/* { dg-final { scan-assembler-times {ipm} 3 } } */