https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102224

--- Comment #13 from CVS Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>:

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

commit r12-3413-ga7b626d98a9a821ffb33466818d6aa86cac1d6fd
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Wed Sep 8 11:25:31 2021 +0200

    i386: Fix up @xorsign<mode>3_1 [PR102224]

    As the testcase shows, we miscompile @xorsign<mode>3_1 if both input
    operands are in the same register, because the splitter overwrites op1
    before with op1 & mask before using op0.

    For dest = xorsign op0, op0 we can actually simplify it from
    dest = (op0 & mask) ^ op0 to dest = op0 & ~mask (aka abs).

    The expander change is an optimization improvement, if we at expansion
    time know it is xorsign op0, op0, we can emit abs right away and get better
    code through that.

    The @xorsign<mode>3_1 is a fix for the case where xorsign wouldn't be known
    to have same operands during expansion, but during RTL optimizations they
    would appear.  For non-AVX we need to use earlyclobber, we require
    dest and op1 to be the same but op0 must be different because we overwrite
    op1 first.  For AVX the constraints ensure that at most 2 of the 3 operands
    may be the same register and if both inputs are the same, handles that
case.
    This case can be easily tested with the xorsign<mode>3 expander change
    reverted.

    Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

    Thinking about it more this morning, while this patch fixes the problems
    revealed in the testcase, the recent PR89984 change was buggy too, but
    perhaps that can be fixed incrementally.  Because for AVX the new code
    destructively modifies op1.  If that is different from dest, say on:
    float
    foo (float x, float y)
    {
      return x * __builtin_copysignf (1.0f, y) + y;
    }
    then we get after RA:
    (insn 8 7 9 2 (set (reg:SF 20 xmm0 [orig:82 _2 ] [82])
            (unspec:SF [
                    (reg:SF 20 xmm0 [88])
                    (reg:SF 21 xmm1 [89])
                    (mem/u/c:V4SF (symbol_ref/u:DI ("*.LC0") [flags 0x2]) [0 
S16 A128])
                ] UNSPEC_XORSIGN)) "hohoho.c":4:12 649 {xorsignsf3_1}
         (nil))
    (insn 9 8 15 2 (set (reg:SF 20 xmm0 [87])
            (plus:SF (reg:SF 20 xmm0 [orig:82 _2 ] [82])
                (reg:SF 21 xmm1 [89]))) "hohoho.c":4:44 1021 {*fop_sf_comm}
         (nil))
    but split the xorsign into:
            vandps  .LC0(%rip), %xmm1, %xmm1
            vxorps  %xmm0, %xmm1, %xmm0
    and then the addition:
            vaddss  %xmm1, %xmm0, %xmm0
    which means we miscompile it - instead of adding y in the end we add
    __builtin_copysignf (0.0f, y).
    So, wonder if we don't want instead in addition to the &Yv <- Yv, 0
    alternative (enabled for both pre-AVX and AVX as in this patch) the
    &Yv <- Yv, Yv where destination must be different from inputs and another
    Yv <- Yv, Yv where it can be the same but then need a match_scratch
    (with X for the other alternatives and =Yv for the last one).
    That way we'd always have a safe register we can store the op1 & mask
    value into, either the destination (in the first alternative known to
    be equal to op1 which is needed for non-AVX but ok for AVX too), in the
    second alternative known to be different from both inputs and in the third
    which could be used for those
    float bar (float x, float y) { return x * __builtin_copysignf (1.0f, y); }
    cases where op1 is naturally xmm1 and dest == op0 naturally xmm0 we'd use
    some other register like xmm2.

    2021-09-08  Jakub Jelinek  <ja...@redhat.com>

            PR target/102224
            * config/i386/i386.md (xorsign<mode>3): If operands[1] is equal to
            operands[2], emit abs<mode>2 instead.
            (@xorsign<mode>3_1): Add early-clobbers for output operand, enable
            first alternative even for avx, add another alternative with
            =&Yv <- 0, Yv, Yvm constraints.
            * config/i386/i386-expand.c (ix86_split_xorsign): If op0 is equal
            to op1, emit vpandn instead.

            * gcc.dg/pr102224.c: New test.
            * gcc.target/i386/avx-pr102224.c: New test.

Reply via email to