This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c++/64380] New: Missed optimization: smarter dead store elimination in dtors


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

            Bug ID: 64380
           Summary: Missed optimization: smarter dead store elimination in
                    dtors
           Product: gcc
           Version: 5.0
            Status: UNCONFIRMED
          Severity: minor
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: petschy at gmail dot com

Some of the stores are eliminated in dtors already, if the values are not used
later. But if there are function calls after the stores, then they are not
eliminated. I see the reason for this, but some functions are special, eg
free(), operator delete and others with the same semantics: they won't crawl
back and access these variables, so if the vars are not used locally, and no
other functions are called, the stores could be eliminated. 

This would be useful eg for classes where there is a user callable function
that releases some/all resources, while keeping the instance alive, and the
dtor calls the same function to release all resources. In this latter case,
stores that are otherwise needed to have a proper state can be omitted since
the instance is being destroyed, anyway.

This is a minor issue probably, since the program shouldn't spend most of its
time running dtors. However, some function attribute symmetric in spirit to
'malloc' would be nice: eg 'free': this would mean that if called, it won't
reach back to the variables of the calling scope, either through its arguments
or through global variables, so those stores could be safely eliminated that
are otherwise dead.

g++-5.0.0 -v
Using built-in specs.
COLLECT_GCC=g++-5.0.0
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-unknown-linux-gnu/5.0.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: ../configure --enable-languages=c,c++ --disable-multilib
--program-suffix=-5.0.0
Thread model: posix
gcc version 5.0.0 20141222 (experimental) (GCC) 

Compliled with
g++-5.0.0 -g -O3 -Wall 20141222-dtor-deadstore.cpp

Dump of assembler code for function test_ra(Foo*):
   0x00000000004005f0 <+0>:     push   %rbp
   0x00000000004005f1 <+1>:     push   %rbx
   0x00000000004005f2 <+2>:     mov    %rdi,%rbp
   0x00000000004005f5 <+5>:     sub    $0x8,%rsp
# two stores before the loop
   0x00000000004005f9 <+9>:     movl   $0x1,0x10(%rdi)
   0x0000000000400600 <+16>:    movl   $0x2,0x14(%rdi)
   0x0000000000400607 <+23>:    mov    0x8(%rdi),%rdi
   0x000000000040060b <+27>:    test   %rdi,%rdi
   0x000000000040060e <+30>:    je     0x400620 <test_ra(Foo*)+48>
   0x0000000000400610 <+32>:    mov    (%rdi),%rbx
   0x0000000000400613 <+35>:    callq  0x4004d0 <_ZdlPv@plt>
   0x0000000000400618 <+40>:    test   %rbx,%rbx
   0x000000000040061b <+43>:    mov    %rbx,%rdi
   0x000000000040061e <+46>:    jne    0x400610 <test_ra(Foo*)+32>
# two stores after the loop, so far so good
   0x0000000000400620 <+48>:    movq   $0x0,0x8(%rbp)
   0x0000000000400628 <+56>:    movl   $0x3,0x18(%rbp)
   0x000000000040062f <+63>:    add    $0x8,%rsp
   0x0000000000400633 <+67>:    pop    %rbx
   0x0000000000400634 <+68>:    pop    %rbp
   0x0000000000400635 <+69>:    retq   

Dump of assembler code for function test_dtor(Foo*):
# two stores before the lop in the dtor. these won't ever be read again
# could be eliminated
   0x0000000000400640 <+0>:     movl   $0x1,0x10(%rdi)
   0x0000000000400647 <+7>:     movl   $0x2,0x14(%rdi)
   0x000000000040064e <+14>:    mov    0x8(%rdi),%rdi
   0x0000000000400652 <+18>:    test   %rdi,%rdi
   0x0000000000400655 <+21>:    je     0x400671 <test_dtor(Foo*)+49>
   0x0000000000400657 <+23>:    push   %rbx
   0x0000000000400658 <+24>:    nopl   0x0(%rax,%rax,1)
   0x0000000000400660 <+32>:    mov    (%rdi),%rbx
   0x0000000000400663 <+35>:    callq  0x4004d0 <_ZdlPv@plt>
   0x0000000000400668 <+40>:    test   %rbx,%rbx
   0x000000000040066b <+43>:    mov    %rbx,%rdi
   0x000000000040066e <+46>:    jne    0x400660 <test_dtor(Foo*)+32>
# no stores here, the ones after 'delete' were eliminated successfully
   0x0000000000400670 <+48>:    pop    %rbx
   0x0000000000400671 <+49>:    repz retq 

Dump of assembler code for function test_dtor2(Foo*):
   0x0000000000400680 <+0>:     push   %rbp
   0x0000000000400681 <+1>:     push   %rbx
   0x0000000000400682 <+2>:     mov    %rdi,%rbp
   0x0000000000400685 <+5>:     sub    $0x8,%rsp
# 4 dead stores in the src, the one to the same addr is eliminated
   0x0000000000400689 <+9>:     movl   $0xc0,(%rdi)
   0x000000000040068f <+15>:    movl   $0x1,0x10(%rdi)
   0x0000000000400696 <+22>:    movl   $0x2,0x14(%rdi)
   0x000000000040069d <+29>:    mov    0x8(%rdi),%rdi
   0x00000000004006a1 <+33>:    test   %rdi,%rdi
   0x00000000004006a4 <+36>:    je     0x4006c0 <test_dtor2(Foo*)+64>
   0x00000000004006a6 <+38>:    nopw   %cs:0x0(%rax,%rax,1)
   0x00000000004006b0 <+48>:    mov    (%rdi),%rbx
   0x00000000004006b3 <+51>:    callq  0x4004d0 <_ZdlPv@plt>
   0x00000000004006b8 <+56>:    test   %rbx,%rbx
   0x00000000004006bb <+59>:    mov    %rbx,%rdi
   0x00000000004006be <+62>:    jne    0x4006b0 <test_dtor2(Foo*)+48>
# stores after 'delete' are eliminated
   0x00000000004006c0 <+64>:    add    $0x8,%rsp
   0x00000000004006c4 <+68>:    mov    %rbp,%rdi
   0x00000000004006c7 <+71>:    pop    %rbx
   0x00000000004006c8 <+72>:    pop    %rbp
   0x00000000004006c9 <+73>:    jmpq   0x4004d0 <_ZdlPv@plt>
End of assembler dump.

----8<----8<----8<---
struct Node
{
        Node*   next;
        char*   cur;
        char    beg[0];
};

struct Base
{
        int     b0;
        int     b1;

        ~Base() { Clear(); }

        void Clear()
        {
                b0 = 0xb0; // OK, these are dead store eliminated (DSE)
                b1 = 0xb1;
        }
};

struct Foo : Base
{
        Node*   nodes;
        int     m1;
        int     m2;
        int     m3;

        ~Foo() { ReleaseAll(false); }

        void ReleaseAll(bool k)
        {
                m1 = 1; // should be DSE'd if called from the dtor, but it's
not
                m2 = 2; // ditto
                Node* n = nodes;
                while (n) {
                        Node* t = n->next;
                        if (!t  &&  k) {
                                n->cur = n->beg;
                                break;
                        }
                        delete n;
                        n = t;
                }
                nodes = n; // OK, DSE'd if called from the dtor
                m3 = 3;    // ditto
        }
};

void test_ra(Foo* f)
{
        f->ReleaseAll(false);
}

void test_dtor(Foo* f)
{
        f->~Foo();
}

void test_dtor2(Foo* f)
{
        f->b0 = 0xc0;   // should be DSE'd, but it's not
        f->m1 = 42;     // OK, DSE'd via the ReleaseAll(): m1=1 store
        delete f;
}

int main()
{
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]