Bug 31070 - Abnormal Jump in Execution for builtin function using 'stepi' Command in GDB
Summary: Abnormal Jump in Execution for builtin function using 'stepi' Command in GDB
Status: RESOLVED NOTABUG
Alias: None
Product: gdb
Classification: Unclassified
Component: gdb (show other bugs)
Version: unknown
: P2 critical
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-11-16 07:24 UTC by Anonymous
Modified: 2023-11-16 11:59 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Anonymous 2023-11-16 07:24:54 UTC
$ gcc --version
gcc (GCC) 13.0.0 20221107 (experimental)
Copyright (C) 2022 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.

$ gdb --version
GNU gdb (GDB) 14.0.50.20230417-git
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ gdb -q a.out
Reading symbols from a.out...
(gdb) b main
Breakpoint 1 at 0x401040: file small.c, line 29.
(gdb) r
Starting program: /root/devil/a.out 

Breakpoint 1, main () at small.c:29
29        __builtin_prefetch (glob_int_arr, 0, 0);
(gdb) si
0x0000000000401044 in simple_global () at small.c:30
30        __builtin_prefetch (glob_ptr_int, 0, 0);
(gdb) si
0x000000000040104b in main () at small.c:151
151       exit (0);
(gdb) 

=================================================
When using the 'stepi' command in GDB, an abnormal jump in the execution occurs when reaching line 30. The execution unexpectedly jumps to line 151.

Additional Information: Source-level stepping (e.g., 'next' or 'step') works fine without any abnormal jumps. 
I was wondering that this might be a bug in gdb. 





This is the test program in gcc test suite (gcc-12.1.0/gcc/testsuite/gcc.c-torture/execute/builtin-prefetch-2.c). 

$ cat small.c
/* Test that __builtin_prefetch does no harm.

   Prefetch data using a variety of storage classes and address
   expressions.  */

int glob_int_arr[100];
int *glob_ptr_int = glob_int_arr;
int glob_int = 4;

static stat_int_arr[100];
static int *stat_ptr_int = stat_int_arr;
static int stat_int;

struct S {
  int a;
  short b, c;
  char d[8];
  struct S *next;
};

struct S str;
struct S *ptr_str = &str;

/* Prefetch global variables using the address of the variable.  */

void
simple_global ()
{
  __builtin_prefetch (glob_int_arr, 0, 0);
  __builtin_prefetch (glob_ptr_int, 0, 0);
  __builtin_prefetch (&glob_int, 0, 0);
}

/* Prefetch file-level static variables using the address of the variable.  */

void
simple_file ()
{
  __builtin_prefetch (stat_int_arr, 0, 0);
  __builtin_prefetch (stat_ptr_int, 0, 0);
  __builtin_prefetch (&stat_int, 0, 0);
}

/* Prefetch local static variables using the address of the variable.  */

void
simple_static_local ()
{
  static int gx[100];
  static int *hx = gx;
  static int ix;
  __builtin_prefetch (gx, 0, 0);
  __builtin_prefetch (hx, 0, 0);
  __builtin_prefetch (&ix, 0, 0);
}

/* Prefetch local stack variables using the address of the variable.  */

void
simple_local ()
{
  int gx[100];
  int *hx = gx;
  int ix;
  __builtin_prefetch (gx, 0, 0);
  __builtin_prefetch (hx, 0, 0);
  __builtin_prefetch (&ix, 0, 0);
}

/* Prefetch arguments using the address of the variable.  */

void
simple_arg (int g[100], int *h, int i)
{
  __builtin_prefetch (g, 0, 0);
  __builtin_prefetch (h, 0, 0);
  __builtin_prefetch (&i, 0, 0);
}

/* Prefetch using address expressions involving global variables.  */

void
expr_global (void)
{
  __builtin_prefetch (&str, 0, 0);
  __builtin_prefetch (ptr_str, 0, 0);
  __builtin_prefetch (&str.b, 0, 0);
  __builtin_prefetch (&ptr_str->b, 0, 0);
  __builtin_prefetch (&str.d, 0, 0);
  __builtin_prefetch (&ptr_str->d, 0, 0);
  __builtin_prefetch (str.next, 0, 0);
  __builtin_prefetch (ptr_str->next, 0, 0);
  __builtin_prefetch (str.next->d, 0, 0);
  __builtin_prefetch (ptr_str->next->d, 0, 0);

  __builtin_prefetch (&glob_int_arr, 0, 0);
  __builtin_prefetch (glob_ptr_int, 0, 0);
  __builtin_prefetch (&glob_int_arr[2], 0, 0);
  __builtin_prefetch (&glob_ptr_int[3], 0, 0);
  __builtin_prefetch (glob_int_arr+3, 0, 0);
  __builtin_prefetch (glob_int_arr+glob_int, 0, 0);
  __builtin_prefetch (glob_ptr_int+5, 0, 0);
  __builtin_prefetch (glob_ptr_int+glob_int, 0, 0);
}

/* Prefetch using address expressions involving local variables.  */

void
expr_local (void)
{
  int b[10];
  int *pb = b;
  struct S t;
  struct S *pt = &t;
  int j = 4;

  __builtin_prefetch (&t, 0, 0);
  __builtin_prefetch (pt, 0, 0);
  __builtin_prefetch (&t.b, 0, 0);
  __builtin_prefetch (&pt->b, 0, 0);
  __builtin_prefetch (&t.d, 0, 0);
  __builtin_prefetch (&pt->d, 0, 0);
  __builtin_prefetch (t.next, 0, 0);
  __builtin_prefetch (pt->next, 0, 0);
  __builtin_prefetch (t.next->d, 0, 0);
  __builtin_prefetch (pt->next->d, 0, 0);

  __builtin_prefetch (&b, 0, 0);
  __builtin_prefetch (pb, 0, 0);
  __builtin_prefetch (&b[2], 0, 0);
  __builtin_prefetch (&pb[3], 0, 0);
  __builtin_prefetch (b+3, 0, 0);
  __builtin_prefetch (b+j, 0, 0);
  __builtin_prefetch (pb+5, 0, 0);
  __builtin_prefetch (pb+j, 0, 0);
}

int
main ()
{
  simple_global ();
  simple_file ();
  simple_static_local ();
  simple_local ();
  simple_arg (glob_int_arr, glob_ptr_int, glob_int);

  str.next = &str;
  expr_global ();
  expr_local ();

  exit (0);
}
Comment 1 Anonymous 2023-11-16 07:27:22 UTC
The command used for the compilation is:

$ gcc -O2 -g small.c
Comment 2 Anonymous 2023-11-16 08:26:17 UTC
Here is the reduced version of the code that triggers the bug:


$ cat small.c
int glob_int_arr[100];
int *glob_ptr_int = glob_int_arr;

void simple_global ()
{
  __builtin_prefetch (glob_int_arr, 0, 0);
  __builtin_prefetch (glob_ptr_int, 0, 0);
}

int main()
{
  simple_global ();
  exit (0);
}


$ gcc -w -O2 -g small.c; gdb -q a.out
Reading symbols from a.out...
(gdb) b main
Breakpoint 1 at 0x401040: file small.c, line 6.
(gdb) r
Starting program: /root/devil/a.out 

Breakpoint 1, main () at small.c:6
6         __builtin_prefetch (glob_int_arr, 0, 0);
(gdb) si
0x0000000000401044 in simple_global () at small.c:7
7         __builtin_prefetch (glob_ptr_int, 0, 0);
(gdb) si
0x000000000040104b in main () at small.c:13
13        exit (0);
(gdb) si
0x000000000040104d in simple_global () at small.c:6
6         __builtin_prefetch (glob_int_arr, 0, 0);
(gdb)
Comment 3 Anonymous 2023-11-16 08:33:00 UTC
I have verified that the latest trunk versions of GCC and GDB still exhibit the same problem.


$ gcc --version
gcc (GCC) 14.0.0 20231116 (experimental)
Copyright (C) 2023 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.

$ gdb --version
GNU gdb (GDB) 15.0.50.20231116-git
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Comment 4 Tom de Vries 2023-11-16 11:36:19 UTC
I can reproduce this:
...
Temporary breakpoint 1, main () at small.c:10
10	  __builtin_prefetch (glob_int_arr, 0, 0);
(gdb) si
0x0000000000401044 in simple_global () at small.c:11
11	  __builtin_prefetch (glob_ptr_int, 0, 0);
(gdb) si
0x000000000040104b in main () at small.c:19
19	  exit (0);
(gdb) si
0x000000000040104d in simple_global () at small.c:10
10	  __builtin_prefetch (glob_int_arr, 0, 0);
(gdb) si
11	  __builtin_prefetch (glob_ptr_int, 0, 0);
...

When decoding the line numbers I see:
...
small.c                                       11            0x401044        
small.c                                       19            0x40104b        
small.c                                       10            0x40104d        
small.c                                       11            0x401054               ...
which perfectly matches the progression we see.

So gdb doesn't do anything wrong, it accurately show the line info as generated by gcc.

So why does the insn at 0x40104b have line number 19?

The disassembly is:
...
0000000000401040 <main>:
  401040:	48 83 ec 08          	sub    $0x8,%rsp
  401044:	48 8b 05 cd 2f 00 00 	mov    0x2fcd(%rip),%rax        # 404018 <glob_ptr_int>
  40104b:	31 ff                	xor    %edi,%edi
  40104d:	0f 18 05 ec 2f 00 00 	prefetchnta 0x2fec(%rip)        # 404040 <glob_int_arr>
  401054:	0f 18 00             	prefetchnta (%rax)
  401057:	e8 d4 ff ff ff       	call   401030 <exit@plt>
  40105c:	0f 1f 40 00          	nopl   0x0(%rax)
...
and the call to exit is represented by these two insns:
...
  40104b:	31 ff                	xor    %edi,%edi
  401057:	e8 d4 ff ff ff       	call   401030 <exit@plt>
...

Optimization separated the two insns and that causes the "jumping" through the lines.

AFAICT, this falls in the range of expected behaviour when debugging optimized code.
Comment 5 Anonymous 2023-11-16 11:54:21 UTC
(In reply to Tom de Vries from comment #4)
> I can reproduce this:
> ...
> Temporary breakpoint 1, main () at small.c:10
> 10	  __builtin_prefetch (glob_int_arr, 0, 0);
> (gdb) si
> 0x0000000000401044 in simple_global () at small.c:11
> 11	  __builtin_prefetch (glob_ptr_int, 0, 0);
> (gdb) si
> 0x000000000040104b in main () at small.c:19
> 19	  exit (0);
> (gdb) si
> 0x000000000040104d in simple_global () at small.c:10
> 10	  __builtin_prefetch (glob_int_arr, 0, 0);
> (gdb) si
> 11	  __builtin_prefetch (glob_ptr_int, 0, 0);
> ...
> 
> When decoding the line numbers I see:
> ...
> small.c                                       11            0x401044        
> small.c                                       19            0x40104b        
> small.c                                       10            0x40104d        
> small.c                                       11            0x401054        
> ...
> which perfectly matches the progression we see.
> 
> So gdb doesn't do anything wrong, it accurately show the line info as
> generated by gcc.
> 
> So why does the insn at 0x40104b have line number 19?
> 
> The disassembly is:
> ...
> 0000000000401040 <main>:
>   401040:	48 83 ec 08          	sub    $0x8,%rsp
>   401044:	48 8b 05 cd 2f 00 00 	mov    0x2fcd(%rip),%rax        # 404018
> <glob_ptr_int>
>   40104b:	31 ff                	xor    %edi,%edi
>   40104d:	0f 18 05 ec 2f 00 00 	prefetchnta 0x2fec(%rip)        # 404040
> <glob_int_arr>
>   401054:	0f 18 00             	prefetchnta (%rax)
>   401057:	e8 d4 ff ff ff       	call   401030 <exit@plt>
>   40105c:	0f 1f 40 00          	nopl   0x0(%rax)
> ...
> and the call to exit is represented by these two insns:
> ...
>   40104b:	31 ff                	xor    %edi,%edi
>   401057:	e8 d4 ff ff ff       	call   401030 <exit@plt>
> ...
> 
> Optimization separated the two insns and that causes the "jumping" through
> the lines.
> 
> AFAICT, this falls in the range of expected behaviour when debugging
> optimized code.

Thank you for your feedback. Following your advice, I have officially reported this issue to the GCC Bugzilla for further investigation.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112565
Comment 6 Tom de Vries 2023-11-16 11:56:23 UTC
(In reply to Anonymous from comment #5)
> Thank you for your feedback. Following your advice,

That was not my advice.

> I have officially
> reported this issue to the GCC Bugzilla for further investigation.
> 
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112565
Comment 7 Anonymous 2023-11-16 11:59:24 UTC
(In reply to Tom de Vries from comment #6)
> (In reply to Anonymous from comment #5)
> > Thank you for your feedback. Following your advice,
> 
> That was not my advice.
> 
> > I have officially
> > reported this issue to the GCC Bugzilla for further investigation.
> > 
> > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112565

I apologize for the misunderstanding. I didn't notice that you mentioned this behavior was expected.