Bug 10003 - gcc -g mis-places low_pc of inlined functions
Summary: gcc -g mis-places low_pc of inlined functions
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 3.3
: P3 normal
Target Milestone: 4.0.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2003-03-08 14:26 UTC by werner
Modified: 2005-07-01 19:24 UTC (History)
2 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu
Known to work:
Known to fail:
Last reconfirmed: 2003-12-20 08:22:26


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description werner 2003-03-08 14:26:01 UTC
This one it pretty nasty: if we place a breakpoint at an inlined
function's low_pc, the arguments aren't even on the stack yet !

Release:
3.3 20030303 (prerelease)

Environment:
System: Linux ar 2.4.18 #5 Mon Mar 18 09:25:24 ART 2002 i686 unknown
Architecture: i686

	
host: i686-pc-linux-gnu
build: i686-pc-linux-gnu
target: i686-pc-linux-gnu
configured with: ./configure --prefix=/usr/local/gcc20030303/ --enable-languages=c

How-To-Repeat:
Example (with Red Hat's gcc 3.1 20011127 on ia32):

$ cat <<EOF >foo.c
int xyz;

static inline int foo(int bar)
{
label:
        xyz = bar;
}

void main(int argc)
{
    foo(123);
}
EOF
$ gcc -w -g foo.c       # gcc 3.3: gcc -w -g -finline foo.c
$ readelf -w a.out
...
 <2><4a>: Abbrev Number: 4 (DW_TAG_inlined_subroutine)
     DW_AT_abstract_origin: <68>
     DW_AT_low_pc      : 0x80483a8 134513576
...
$ objdump -d a.out 
...
08048398 <main>:
...
 80483a6:       29 c4                   sub    %eax,%esp
 80483a8:       c7 45 fc 7b 00 00 00    movl   $0x7b,0xfffffffc(%ebp)
 80483af:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
 80483b2:       a3 34 95 04 08          mov    %eax,0x8049534
 80483b7:       c9                      leave
...
Comment 1 werner 2003-03-08 14:26:01 UTC
Fix:
Work-around: don't access arguments in inlined functions ?
Comment 2 Andrew Pinski 2003-06-07 21:51:16 UTC
Sill happens on the mainline (20030607). I think the problem is the tree inliner is setting 
the line and file for the passing of the variables to be the same as the inlined functions.  It 
also affects stabs debugging.
Comment 3 Andrew Pinski 2003-06-14 16:29:28 UTC
I am moving this to middle-end because it a tree-inliner problem. It looks like the 
initializers' line is set to the begining line of the function that is being inlined.

From the comment in tree-inline.c this looks intenational but who knows:
  /* Expand any inlined calls in the initializers.  Do this before we
     push FN on the stack of functions we are inlining; we want to
     inline calls to FN that appear in the initializers for the
     parameters.  */
Comment 4 Andrew Pinski 2003-06-17 23:08:57 UTC
*** Bug 4431 has been marked as a duplicate of this bug. ***
Comment 5 Steven Bosscher 2003-07-10 12:10:29 UTC
Hmm, this is said to be a tree-inliner bug (hence middle-end instead of debug),
but the bug is reported against a GNU CC version that didn't even use the
tree-inliner...  Maybe it's something else after all?
Comment 6 Andrew Pinski 2003-07-10 12:28:35 UTC
Tree inlining was added to c on 2001-10-04 so it was reported against a gcc which had tree 
inlining but any way look at this rtl (which is the *.01.rtl):

(note 8 7 9 ("pr10003.c") 11)

(note 9 8 10 ("pr10003.c") 4)

(note 10 9 11 0x1047a80 NOTE_INSN_BLOCK_BEG)

(insn 11 10 12 (set (reg:SI 118)
        (const_int 123 [0x7b])) -1 (nil)
    (nil))

(insn 12 11 13 (set (mem/f:SI (reg/f:SI 114 virtual-stack-vars) [0 bar+0 S4 A32])
        (reg:SI 118)) -1 (nil)
    (nil))

(note 13 12 14 0x1047ab0 NOTE_INSN_BLOCK_BEG)

(note 14 13 15 NOTE_INSN_DELETED)

(note 15 14 16 ("pr10003.c") 6)

See the second note is wrong here so it still is a problem with the middle end because the rtl has 
not loaded 123 yet.
Comment 7 Andrew Pinski 2003-07-10 12:39:34 UTC
I gave the wrong rtl:
(note 8 7 9 ("pr10003.c") 11)

(note 9 8 10 ("pr10003.c") 4)

(note 10 9 11 0x1047ab0 NOTE_INSN_BLOCK_BEG)

(insn 11 10 12 (set (reg/v:SI 120 [ bar ])
        (const_int 123 [0x7b])) -1 (nil)
    (nil))

(note 12 11 13 0x1047ae0 NOTE_INSN_BLOCK_BEG)

(note 13 12 14 NOTE_INSN_DELETED)

(note 14 13 15 ("pr10003.c") 6)

It is still wrong, I think both the tree and the rtl inliners are wrong.
Comment 8 Andrew Pinski 2003-09-17 18:34:46 UTC
*** Bug 12319 has been marked as a duplicate of this bug. ***
Comment 9 carlo 2003-09-18 01:08:18 UTC
I looked into this, using a little program:

extern int a;

inline void g() { a = a + 1; }

void f() {
  g();
}


Which sports the problem that
the low_pc of the inlined g() is
including the start of f():

# /usr/src/gcc/gcc-cvs-3.4-objdir/gcc/cc1plus -fpreprocessed troep.i -quiet
-dumpbase troep.c -mtune=pentiumpro -auxbase troep -g -O -version -o troep.s
# as -V -Qy -o troep.o troep.s
# objdump -d troep.o
00000000 <_Z1fv>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   ff 05 00 00 00 00       incl   0x0
   9:   5d                      pop    %ebp
   a:   c3                      ret

The "push %ebp" and "mov %esp,%ebp" are included
in the low_pc/high_pc for g():

# readelf --debug-dump troep.o
 <2><3d>: Abbrev Number: 3 (DW_TAG_inlined_subroutine)
     DW_AT_abstract_origin: <4b>
     DW_AT_low_pc      : 0 0
     DW_AT_high_pc     : 0x9 9
 <1><4b>: Abbrev Number: 4 (DW_TAG_subprogram)
     DW_AT_external    : 1
     DW_AT_name        : g


The reason that this happens is because in
gcc/dwarf2out.c function gen_inlined_subroutine_die
the low_pc is generated using

      ASM_GENERATE_INTERNAL_LABEL (label, BLOCK_BEGIN_LABEL,
                                   BLOCK_NUMBER (stmt));
      add_AT_lbl_id (subr_die, DW_AT_low_pc, label);

which translates in that it uses the string ".LBB2"
which is supposedly corresponding with the revelant 'stmt'
block.

The actual place where .LBB2 is emitted then determines
the real value of low_pc.

Looking at the assembly from the troep.s file we see:

_Z1fv:
.LFB4:
        .file 1 "troep.c"
        .loc 1 5 0
.LBB2:
.LBB3:
.LBB4:
        pushl   %ebp
.LCFI0:
        movl    %esp, %ebp
.LCFI1:
        .loc 1 3 0
        incl    a
.LBE4:
.LBE3:
.LBE2:
        .loc 1 7 0
        popl    %ebp
        ret


In other words, the .LBB2 label indeed includes
the 'pushl %ebp' and 'movl %esp, %ebp'.

I started to dump various intermitant RTL code
and found the first occurence of the 'push %ebp'
in the output of -dB :

(note 3 1 30 NOTE_INSN_FUNCTION_BEG)

;; Start of basic block 0, registers live: 6 [bp] 7 [sp] 16 [] 20 [frame]
(note 30 3 34 0 [bb 0] NOTE_INSN_BASIC_BLOCK)

(insn/f 34 30 35 0 troep.c:5 (set (mem:SI (pre_dec:SI (reg/f:SI 7 esp)) [0 S4 A8])
        (reg/f:SI 6 ebp)) 32 {*pushsi2} (nil)
    (nil))

(insn/f 35 34 36 0 troep.c:5 (set (reg/f:SI 6 ebp)
        (reg/f:SI 7 esp)) 38 {*movsi_1} (nil)
    (nil))


Note that already here the start of 'basic block 0'
is BEFORE these instructions.  This seems the only
basic block left - in many debug dumps I see no a clear
appearance of the three blocks that should be involved
according to the final assembly output.

The first dump (.01.rtl) these three blocks are still there:

;; Function void f()

(note 1 0 2 ("troep.c") 5)

(note 2 1 3 NOTE_INSN_DELETED)

(note 3 2 4 NOTE_INSN_FUNCTION_BEG)

(note 4 3 5 NOTE_INSN_DELETED)

(note 5 4 6 0x402a4c30 NOTE_INSN_BLOCK_BEG)

(note 6 5 7 NOTE_INSN_DELETED)

(note 7 6 8 NOTE_INSN_DELETED)

(note 8 7 9 NOTE_INSN_DELETED)

(note 9 8 10 ("troep.c") 6)

(note 10 9 11 NOTE_INSN_DELETED)

(note 11 10 12 NOTE_INSN_DELETED)

(note 12 11 13 ("troep.c") 3)

(note 13 12 14 0x402a4c80 NOTE_INSN_BLOCK_BEG)

(note 14 13 15 0x402a4ca8 NOTE_INSN_BLOCK_BEG)

(note 15 14 16 NOTE_INSN_DELETED)

(note 16 15 17 NOTE_INSN_DELETED)

(note 17 16 18 NOTE_INSN_DELETED)

(note 18 17 19 NOTE_INSN_DELETED)

(note 19 18 20 NOTE_INSN_DELETED)

(insn 20 19 21 (set (reg:SI 59)
        (mem/f:SI (symbol_ref:SI ("a") [flags 0x40] <var_decl 0x402c62f4 a>) [0
a+0 S4 A32])) -1 (nil)
    (nil))
[etc, this is the start of a = a + 1]

But here it is unclear to me where the "push %ebp"
should be.

However, because already the next dump in line
(troep.c.02.sibling) seems to have gotten rid of
two blocks:

;; Function void f()

(note 1 0 3 ("troep.c") 5)

(note 3 1 9 NOTE_INSN_FUNCTION_BEG)

(note 9 3 12 ("troep.c") 6)

(note 12 9 30 ("troep.c") 3)

(note 30 12 20 0 [bb 0] NOTE_INSN_BASIC_BLOCK)

(insn 20 30 21 0 troep.c:3 (set (reg:SI 59)
        (mem/f:SI (symbol_ref:SI ("a") [flags 0x40] <var_decl 0x402c62f4 a>) [0
a+0 S4 A32])) -1 (nil)
    (nil))
[etc]

and where this '(note 30 ...' no doubt is the
inner block and therefore the start of g(),
I would conclude that also the block in the
last dump is this block. (? or the dumps suck).


An important observation is that when we
add persistent code to f(), before g(), like this:

extern int a, b;

inline void g() { a = a + 1; }

void f() {
  b = b + 1;
  g();
}

Then the problem goes away! :

_Z1fv:
.LFB4:
        .file 1 "troep.c"
        .loc 1 5 0
.LBB2:
        pushl   %ebp
.LCFI0:
        movl    %esp, %ebp
.LCFI1:
        .loc 1 6 0
        incl    b
.LBB3:
.LBB4:
        .loc 1 3 0
        incl    a
.LBE4:
.LBE3:
.LBE2:
        .loc 1 8 0
        popl    %ebp
        ret
[...]
        .uleb128 0x3
        .long   0x4b
        .long   .LBB3
        .long   .LBE3

Now the block is .LBB3 till .LBE3
and as you see it does not include
too much anymore!

Therefore I am inclined to think that
the problem lays in the point where
the "push %ebp" and "movl %esp, %ebp"
are emitted; it seems that this is
'delayed' till there is other code
emitted (?) - but as a result is emitted
after the start of a block that was
marking the start of an inlined function!
This of course does not happen in the
case when there is code in f() between
its start and the call to g().

I am sorry I can't be of further help;
I am not famliar with the internals of
gcc at all.  But I hope that this will
help a little in finding the real problem.

Comment 10 carlo 2003-09-20 13:27:48 UTC
Can someone assign this to me please?
I intend to fix it - already working on it.
Comment 11 carlo 2003-09-20 17:04:13 UTC
Werner,

can you please give some feedback on the following?

You write: "if we place a breakpoint at an inlined
function's low_pc, the arguments aren't even on the
stack yet!".  But, this is how it should be - the
readelf output you give shows a DW_TAG_inlined_subroutine
and the low_pc for that should be set at the very start
of the function (include the prologue of the function).

After fixing PR 12319, which certainly needs to be fixed
before this PR can be fixed (if at all needed), I get
the following for your code:

08048314 <main>:
 8048314:       55                      push   %ebp
 8048315:       89 e5                   mov    %esp,%ebp
 8048317:       83 ec 08                sub    $0x8,%esp
 804831a:       83 e4 f0                and    $0xfffffff0,%esp
 804831d:       b8 00 00 00 00          mov    $0x0,%eax
 8048322:       29 c4                   sub    %eax,%esp
 8048324:       c7 45 fc 7b 00 00 00    movl   $0x7b,0xfffffffc(%ebp)
 804832b:       8b 45 fc                mov    0xfffffffc(%ebp),%eax
 804832e:       a3 04 95 04 08          mov    %eax,0x8049504
 8048333:       c9                      leave
 8048334:       c3                      ret

 <2><a56>: Abbrev Number: 4 (DW_TAG_inlined_subroutine)
     DW_AT_abstract_origin: <a87>    (this is 'foo')
     DW_AT_low_pc      : 0x8048324 134513444
     DW_AT_high_pc     : 0x8048333 134513459

and that is correct.
Further I see:

 <2><aa3>: Abbrev Number: 11 (DW_TAG_label)
     DW_AT_name        : label
     DW_AT_decl_file   : 1
     DW_AT_decl_line   : 5
 <4><a74>: Abbrev Number: 7 (DW_TAG_label)
     DW_AT_abstract_origin: <aa3>
     DW_AT_low_pc      : 0x804832b 134513451

And 0x804832b is *after* the prologue, also ok thus.

I fail to set a breakpoint on 'label' with gdb though
nor do I seem to be able to print 'bar' at any instruction
with gdb. But that is a problem of gdb, not of the DWARF2
info generated with gcc.

Don't you think that when PR 12319 is fixed, this PR can be
closed too?

Comment 12 Steven Bosscher 2005-07-01 19:17:07 UTC
Is anyone still looking at this?  Do things work as they are supposed 
to on mainline now? 
 
Comment 13 Andrew Pinski 2005-07-01 19:24:22 UTC
This was fixed on the mainline and 4.0.0.