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

            Bug ID: 127665
           Summary: [16.1] x86-64 -O0: deferred-length character
                    assignment of length 64 is never copied (memmove
                    expands to a lone compare)
           Product: gcc
           Version: 16.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: federico.perini at gmail dot com
  Target Milestone: ---

Filing this only as a reference for anyone who hits it on 16.1, as it seems to
be
fixed already (Works on Compiler explorer with any other version:
https://godbolt.org/z/6Pe95471a), I could not find an existing report. 
Please close it as fixed or as a duplicate as appropriate.

At -O0 on x86-64, gfortran 16.1.0 drops the copy when a value of constant
length 64 is assigned to a deferred-length character variable. The variable
is allocated with the right length (malloc(64)) but its buffer is never
written, so it keeps undefined heap contents. There is no diagnostic.

Testcase (pr.f90):

module len64_mod
    implicit none
contains
    logical function copy63(t) result(ok)
        character(len=63), intent(in) :: t
        character(:), allocatable :: s
        s = t
        ok = (len(s) == 63 .and. s == t)
    end function copy63

    logical function copy64(t) result(ok)
        character(len=64), intent(in) :: t
        character(:), allocatable :: s
        s = t
        ok = (len(s) == 64 .and. s == t)
    end function copy64

    logical function copy65(t) result(ok)
        character(len=65), intent(in) :: t
        character(:), allocatable :: s
        s = t
        ok = (len(s) == 65 .and. s == t)
    end function copy65
end module len64_mod

program len64
    use len64_mod
    if (.not.copy63(repeat('q', 63))) stop 1
    if (.not.copy64(repeat('q', 64))) stop 2
    if (.not.copy65(repeat('q', 65))) stop 3
    stop 0
end program len64

Results with gfortran 16.1.0 (x86_64-w64-mingw32, MSYS2 Rev5):

  gfortran -O0 pr.f90                                          -> STOP 2
  gfortran -O0 -mmemcpy-strategy=libcall:-1:noalign pr.f90     -> STOP 0
  gfortran -Og / -O1 / -O2 / -O3 pr.f90                        -> STOP 0

It also fails with 16.1 for x86_64-linux-gnu on Compiler Explorer, and passes
with 16.2.0 there. Lengths 63 and 65 pass; in our testing every length from
1 to 300 except 64 copies correctly. Copying into an allocatable dummy or an
allocatable component, or assigning trim(t), also copies correctly.

Analysis: the tree dump is correct. For a destination longer than 64 it
copies 64 bytes and pads with blanks; otherwise it calls
__builtin_memmove (dst, src, len). At -O0 the x86 expansion of that second
memmove is a single compare with no branch and no move (copy64, -O0 -S):

        cmpq    $64, %rax
        jle     .L16
        movq    %r12, %rax        # len > 64: 8 x movq copy, then blank pad
        movq    (%rdx), %rsi
        ...
.L16:
        cmpl    $32, %eax         # len <= 64: memmove(dst, src, len) --
nothing else
.L15:
        movq    -16(%rbp), %rax   # next statement
        ...

-mmemcpy-strategy=libcall:-1:noalign restores the copy (a memmove call).

gfortran -v:
Target: x86_64-w64-mingw32
Thread model: posix
gcc version 16.1.0 (Rev5, Built by MSYS2 project)

Thank you,
Federico
  • [Bug target/127665] New: [16... federico.perini at gmail dot com via Gcc-bugs

Reply via email to