aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuergen Christ <jchrist@linux.ibm.com>2023-11-20 09:13:10 +0100
committerAndreas Krebbel <krebbel@linux.ibm.com>2023-11-23 15:32:17 +0100
commit466b100e5fee808d77598e0f294654deec281150 (patch)
tree68392e0d1683a25422c10694ca4fc51603cbe4d7
parent111b5555c7a37f0bcf41c27363bbe8acbb6eb238 (diff)
downloadgcc-466b100e5fee808d77598e0f294654deec281150.zip
gcc-466b100e5fee808d77598e0f294654deec281150.tar.gz
gcc-466b100e5fee808d77598e0f294654deec281150.tar.bz2
s390: implement flags output
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>
-rw-r--r--gcc/config/s390/s390-c.cc1
-rw-r--r--gcc/config/s390/s390.cc139
-rw-r--r--gcc/config/s390/s390.md8
-rw-r--r--gcc/doc/extend.texi5
-rw-r--r--gcc/testsuite/gcc.target/s390/ccor.c88
5 files changed, 232 insertions, 9 deletions
diff --git a/gcc/config/s390/s390-c.cc b/gcc/config/s390/s390-c.cc
index fce56934..7e455ab 100644
--- a/gcc/config/s390/s390-c.cc
+++ b/gcc/config/s390/s390-c.cc
@@ -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
diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index e36efec..b3603be 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -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);
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 4aa4a94..e30795a 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -6928,7 +6928,7 @@
[(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]);
@@ -6937,15 +6937,15 @@
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)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 6dbe2a9..1ae589a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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
index 0000000..31f30f6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/ccor.c
@@ -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 } } */