This is the mail archive of the gcc@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]

Re: Missed optimization with const member


On Wed, Jul 5, 2017 at 12:14 PM, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> On 5 July 2017 at 10:13, Oleg Endo wrote:
>> Hi,
>>
>> On Wed, 2017-07-05 at 02:02 +0200, Geza Herman wrote:
>>>
>>> Here's what happens: in callInitA(), an Object put onto the stack (which
>>> has a const member variable, initialized to 0). Then somefunction called
>>> (which is intentionally not defined). Then ~Object() is called, which
>>> has an "if", which has a not-immediately-obvious, but always false
>>> condition. Compiling with -03, everything gets inlined.
>>>
>>> My question is about the inlined ~Object(). As m_initType is always 0,
>>> why does not optimize the destructor GCC away? GCC inserts code that
>>> checks the value of m_initType.
>>>
>>> Is it because such construct is rare in practice? Or is it hard to do an
>>> optimization like that?
>>
>> It's not safe to optimize it away because the compiler does not know
>> what "somefunction" does.  Theoretically, it could cast the "Object&"
>> to some subclass and overwrite the const variable.  "const" does not
>> mean that the memory location is read-only in some way.
>
> No, that would be undefined behaviour. The data member is defined as
> const, so it's not possible to write to that member without undefined
> behaviour. A variable defined with a const type is not the same as a
> variable accessed through a pointer/reference to a const type.
>
> Furthermore, casting the Object to a derived class would also be
> undefined, because the dynamic type is Object, not some derived type.

There's a related PR about this
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67886). basic.lifetime
allows to devirtualize second call to foo in
  Base *p = new Derived;
  p->foo();
  p->foo();
to
  Base *p = new Derived;
  Derived::foo(p);
  Derived::foo(p);
because first call is not allowed to changed dynamic type of p. Clang
optimizes this but GCC does not.

> I think the reason it's not optimized away is for this case:
>
> void somefunction(const Object& object);
> {
>   void* p = &object;
>   object.~Object();
>   new(p) Object();
> }
>
> This means that after calling someFunction there could be a different
> object at the same location (with a possibly different value for that
> member).
>
> However, for this specific case that's also not possible, because
> there are no constructors that could initialize the member to anything
> except zero. But that requires more than just control-flow analysis,
> it also requires analysing all the available constructors to check
> there isn't one that does:
>
> Object::Object(int) : m_initType(1) { }


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