IBM Z: Fix usage of "f" constraint with long doubles
After switching the s390 backend to store long doubles in vector registers, "f" constraint broke when used with the former: long doubles correspond to TFmode, which in combination with "f" corresponds to hard regs %v0-%v15, however, asm users expect a %f0-%f15 pair. Fix by using TARGET_MD_ASM_ADJUST hook to convert TFmode values to FPRX2mode and back. gcc/ChangeLog: 2020-12-14 Ilya Leoshkevich <iii@linux.ibm.com> * config/s390/s390.c (f_constraint_p): New function. (s390_md_asm_adjust): Implement TARGET_MD_ASM_ADJUST. (TARGET_MD_ASM_ADJUST): Likewise. gcc/testsuite/ChangeLog: 2020-12-14 Ilya Leoshkevich <iii@linux.ibm.com> * gcc.target/s390/vector/long-double-asm-commutative.c: New test. * gcc.target/s390/vector/long-double-asm-earlyclobber.c: New test. * gcc.target/s390/vector/long-double-asm-in-out.c: New test. * gcc.target/s390/vector/long-double-asm-inout.c: New test. * gcc.target/s390/vector/long-double-asm-matching.c: New test. * gcc.target/s390/vector/long-double-asm-regmem.c: New test. * gcc.target/s390/vector/long-double-volatile-from-i64.c: New test.
This commit is contained in:
parent
8a6a62614a
commit
3cb8aab390
8 changed files with 190 additions and 0 deletions
|
@ -16708,6 +16708,89 @@ s390_shift_truncation_mask (machine_mode mode)
|
|||
return mode == DImode || mode == SImode ? 63 : 0;
|
||||
}
|
||||
|
||||
/* Return TRUE iff CONSTRAINT is an "f" constraint, possibly with additional
|
||||
modifiers. */
|
||||
|
||||
static bool
|
||||
f_constraint_p (const char *constraint)
|
||||
{
|
||||
for (size_t i = 0, c_len = strlen (constraint); i < c_len;
|
||||
i += CONSTRAINT_LEN (constraint[i], constraint + i))
|
||||
{
|
||||
if (constraint[i] == 'f')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Implement TARGET_MD_ASM_ADJUST hook in order to fix up "f"
|
||||
constraints when long doubles are stored in vector registers. */
|
||||
|
||||
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*/)
|
||||
{
|
||||
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;
|
||||
|
||||
unsigned ninputs = inputs.length ();
|
||||
unsigned noutputs = outputs.length ();
|
||||
for (unsigned i = 0; i < noutputs; i++)
|
||||
{
|
||||
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);
|
||||
gcc_assert (ok);
|
||||
if (!f_constraint_p (constraint))
|
||||
/* Long double with a constraint other than "=f" - nothing to do. */
|
||||
continue;
|
||||
gcc_assert (allows_reg);
|
||||
gcc_assert (!is_inout);
|
||||
/* Copy output value from a FPR pair into a vector register. */
|
||||
rtx fprx2 = gen_reg_rtx (FPRX2mode);
|
||||
push_to_sequence2 (after_md_seq, after_md_end);
|
||||
emit_insn (gen_fprx2_to_tf (outputs[i], fprx2));
|
||||
after_md_seq = get_insns ();
|
||||
after_md_end = get_last_insn ();
|
||||
end_sequence ();
|
||||
outputs[i] = fprx2;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ninputs; i++)
|
||||
{
|
||||
if (GET_MODE (inputs[i]) != TFmode)
|
||||
/* Not a long double - nothing to do. */
|
||||
continue;
|
||||
const char *constraint = constraints[noutputs + i];
|
||||
bool allows_mem, allows_reg;
|
||||
bool ok = parse_input_constraint (&constraint, i, ninputs, noutputs, 0,
|
||||
constraints.address (), &allows_mem,
|
||||
&allows_reg);
|
||||
gcc_assert (ok);
|
||||
if (!f_constraint_p (constraint))
|
||||
/* Long double with a constraint other than "f" (or "=f" for inout
|
||||
operands) - nothing to do. */
|
||||
continue;
|
||||
gcc_assert (allows_reg);
|
||||
/* Copy input value from a vector register into a FPR pair. */
|
||||
rtx fprx2 = gen_reg_rtx (FPRX2mode);
|
||||
emit_insn (gen_tf_to_fprx2 (fprx2, inputs[i]));
|
||||
inputs[i] = fprx2;
|
||||
input_modes[i] = FPRX2mode;
|
||||
}
|
||||
|
||||
return after_md_seq;
|
||||
}
|
||||
|
||||
/* Initialize GCC target structure. */
|
||||
|
||||
#undef TARGET_ASM_ALIGNED_HI_OP
|
||||
|
@ -17015,6 +17098,9 @@ s390_shift_truncation_mask (machine_mode mode)
|
|||
#undef TARGET_MAX_ANCHOR_OFFSET
|
||||
#define TARGET_MAX_ANCHOR_OFFSET 0xfff
|
||||
|
||||
#undef TARGET_MD_ASM_ADJUST
|
||||
#define TARGET_MD_ASM_ADJUST s390_md_asm_adjust
|
||||
|
||||
struct gcc_target targetm = TARGET_INITIALIZER;
|
||||
|
||||
#include "gt-s390.h"
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
long double res, x = 40., y = 2.;
|
||||
asm("lxr\t%0,%1\n"
|
||||
"\taxbr\t%0,%2"
|
||||
: "=&f"(res)
|
||||
: "%f"(x), "f"(y));
|
||||
assert (res == 42.);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
long double res, x = 0x1.0000000000001p+0L,
|
||||
exp = 1.00000000000000011102230246251564788e+0L;
|
||||
asm("lzxr\t%0\n"
|
||||
"\tsqxbr\t%0,%1"
|
||||
: "=&f"(res)
|
||||
: "f"(x));
|
||||
assert (res == exp);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
long double res, x = 0x1.0000000000001p+0L,
|
||||
exp = 1.00000000000000011102230246251564788e+0L;
|
||||
asm("sqxbr\t%0,%1" : "=f"(res) : "f"(x));
|
||||
assert (res == exp);
|
||||
}
|
14
gcc/testsuite/gcc.target/s390/vector/long-double-asm-inout.c
Normal file
14
gcc/testsuite/gcc.target/s390/vector/long-double-asm-inout.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
long double res = 0x1.0000000000001p+0L,
|
||||
exp = 1.00000000000000011102230246251564788e+0L;
|
||||
asm("sqxbr\t%0,%0" : "+f"(res));
|
||||
assert (res == exp);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
long double res, x = 40., y = 2.;
|
||||
asm("axbr\t%0,%2" : "=f"(res) : "0"(x), "f"(y));
|
||||
assert (res == 42.);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch" } */
|
||||
|
||||
void
|
||||
foo (long double x)
|
||||
{
|
||||
asm("# %0" : "+fm"(x));
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O3 -march=z14 -mzarch --save-temps" } */
|
||||
/* { dg-do run { target { s390_z14_hw } } } */
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
__attribute__ ((noipa)) static long double
|
||||
long_double_volatile_from_i64 (int64_t x)
|
||||
{
|
||||
static volatile long double y;
|
||||
y = x;
|
||||
return y;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-assembler-times {\n\tcxgbr\t} 1 } } */
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
assert (long_double_volatile_from_i64 (42) == 42.L);
|
||||
assert (long_double_volatile_from_i64 (-42) == -42.L);
|
||||
}
|
Loading…
Add table
Reference in a new issue