diff options
author | Ilya Leoshkevich <iii@linux.ibm.com> | 2020-12-10 15:43:59 +0100 |
---|---|---|
committer | Ilya Leoshkevich <iii@linux.ibm.com> | 2021-03-08 14:41:39 +0100 |
commit | 3cb8aab390ccf31e4581863b080db30c6735e51e (patch) | |
tree | 94ad54a15d2535fb4ebac9feee12414da40f4556 | |
parent | 8a6a62614a8ae4544770420416d1632d6c3d3f6e (diff) | |
download | gcc-3cb8aab390ccf31e4581863b080db30c6735e51e.zip gcc-3cb8aab390ccf31e4581863b080db30c6735e51e.tar.gz gcc-3cb8aab390ccf31e4581863b080db30c6735e51e.tar.bz2 |
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.
8 files changed, 190 insertions, 0 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index c9aea21..de48271 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -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" diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-commutative.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-commutative.c new file mode 100644 index 0000000..59d807c --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-commutative.c @@ -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.); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-earlyclobber.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-earlyclobber.c new file mode 100644 index 0000000..5dd0275 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-earlyclobber.c @@ -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); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-in-out.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-in-out.c new file mode 100644 index 0000000..27d447f --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-in-out.c @@ -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); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-inout.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-inout.c new file mode 100644 index 0000000..e0b6ac5 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-inout.c @@ -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); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-matching.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-matching.c new file mode 100644 index 0000000..c8b8c3d --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-matching.c @@ -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.); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-asm-regmem.c b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-regmem.c new file mode 100644 index 0000000..314f658 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-asm-regmem.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z14 -mzarch" } */ + +void +foo (long double x) +{ + asm("# %0" : "+fm"(x)); +} diff --git a/gcc/testsuite/gcc.target/s390/vector/long-double-volatile-from-i64.c b/gcc/testsuite/gcc.target/s390/vector/long-double-volatile-from-i64.c new file mode 100644 index 0000000..f448984 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/vector/long-double-volatile-from-i64.c @@ -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); +} |