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