This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c++/64380] New: Missed optimization: smarter dead store elimination in dtors
- From: "petschy at gmail dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Mon, 22 Dec 2014 23:30:32 +0000
- Subject: [Bug c++/64380] New: Missed optimization: smarter dead store elimination in dtors
- Auto-submitted: auto-generated
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()
{
}