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

            Bug ID: 119188
           Summary: Incorrect -fcallgraph-info=su for leaf functions on
                    x86-64
           Product: gcc
           Version: 14.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lukeshu at lukeshu dot com
  Target Milestone: ---

With the given simple strnlen function, I'm seeing -fcallgraph-info report that
the function uses 16 bytes of stack space, but by my count the disassembly
shows it using 8+32=40 bytes.

$ gcc --version
gcc (GCC) 14.2.1 20250207
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ cat f.c
unsigned int _strnlen_s(const char *str, unsigned long long maxsize) {
    const char *s;
    for (s = str; *s && maxsize--; ++s);
    return (unsigned int) (s - str);
}

$ gcc -fcallgraph-info=su -c f.c

$ cat f.ci
graph: { title: "f.c"
node: { title: "_strnlen_s" label: "_strnlen_s\nf.c:1:14\n16 bytes (static)" }
}

$ objdump --disassemble f.o                                                     
f.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_strnlen_s>:
   0:   55                      push   %rbp                     # 8 bytes
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
   8:   48 89 75 e0             mov    %rsi,-0x20(%rbp)         # clearly using
another 32 bytes
   c:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  10:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  14:   eb 05                   jmp    1b <_strnlen_s+0x1b>
  16:   48 83 45 f8 01          addq   $0x1,-0x8(%rbp)
  1b:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  1f:   0f b6 00                movzbl (%rax),%eax
  22:   84 c0                   test   %al,%al
  24:   74 11                   je     37 <_strnlen_s+0x37>
  26:   48 8b 45 e0             mov    -0x20(%rbp),%rax
  2a:   48 8d 50 ff             lea    -0x1(%rax),%rdx
  2e:   48 89 55 e0             mov    %rdx,-0x20(%rbp)
  32:   48 85 c0                test   %rax,%rax
  35:   75 df                   jne    16 <_strnlen_s+0x16>
  37:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  3b:   48 2b 45 e8             sub    -0x18(%rbp),%rax
  3f:   5d                      pop    %rbp
  40:   c3                      ret

If I insert a "dummy" function call to force it to update %rsp before the call,
I get the correct count (now 48 bytes, because of the extra 8 from `call`):

void x(void);

unsigned int _strnlen_s(const char *str, unsigned long long maxsize) {
    const char *s;
    for (s = str; *s && maxsize--; ++s);
    x();
    return (unsigned int) (s - str);
}

Reply via email to