Topic: Constructor temporary compiler optimizations


Author: willhall@chelsea.ios.com (Will Hall)
Date: 1996/11/15
Raw View
In article <55plbt$lvb@netlab.cs.rpi.edu>, mhcox@ptdprolog.net (Michael
H.
Cox) wrote:

> Don't understand why the explicit call to constructor seems to be optimized
> out. I understand
> compilers are free to optimize out any intermediate temporaries their
> initial code generation
> schemes might produce, but I thought anything *I* explicitly specify must
> be honored.

Michael,

Note the following from the C++ draft standard:

3.7.2  Automatic storage duration
( ... )
3 If  a  named  automatic object has initialization or a destructor with
  side effects, it shall not be destroyed before the end of  its  block,
  nor shall it be eliminated as an optimization even if it appears to be
  unused.

(BTW, I read this as: "If a named automatic object has initialization
with
side effects or has a destructor with side effects, ..." as opposed to:
"If a named automatic object has any initialization whatsoever, or has a
destructor with side effect, ...")

This implies the following:

GIVEN:
   a named auto object of class type, where the object appears to be
unused and the class has no user-defined dtor, and where the object is
initialized using direct-initialization via a ctor (or, equivalently,
using copy-initialization via a copy ctor ... see comments on
irrelevance
of op= below)

WE CAN INFER THAT:
   the object may be eliminated as an optimization if (A) the ctor
invoked
for initialization has no side effects, and (B) any expression passed as
an argument to the ctor has no side effects.

What do "side effects" mean? IMO:
   for (A) above, side effects include (but are not ltd to) the named
auto
object in question invoking a ctor which not only takes one or more
arguments, but _uses_ them;
   for (B) above, side effects could be incurred in the evaluation of
any
expressions passed to the ctor.

Here is (a simplified version of) your example:

#include <iostream.h>
class MyClass {
public:
   MyClass(int i) { cerr << "MyClass(int" << "=" << i << ")" << endl; }
   MyClass(const MyClass& rhs) { cerr << "MyClass(const MyClass &)" <<
endl; }
   MyClass & operator=(const MyClass& rhs) { /* whatever -- see comments
on
                                                irrelevance of op= below
*/ }
}
int main(int, char**) {
  MyClass local(MyClass(1));     // direct-initialization
  MyClass local2 = MyClass(2);   // copy-initialization
}


Besides the obvious difference in their signatures, the main difference
between the two ctors is that MyClass(int) actually _uses_ its argument,
whereas the copy ctor does not. Let's examine the first line of main():

   MyClass local(MyClass(1))  // line 1

Here, MyClass local( /* ... */ ) is the named auto object, and
MyClass(1)
is the initialization (with or without side effects).
If instead of this stmt, we had two separate ones:

   MyClass tmp(1);            // line 1.a
   MyClass local(tmp);        // line 1.b

then clearly a compiler would be free to eliminate (1.b). Would it be
free
to eliminate (1.a)? That depends on whether MyClass::ctor(int) incurs
"side effects" just because it uses its argument and despite the fact
that
it doesn't alter it.

Because (1.a) is the initialization component of (1), the answer to
whether (1) can be eliminated is the same as the answer to whether
MyClass::ctor(int) is side-effect-free. I'm not sure what the answer to
this is, but these are two distinct cases.

HOWEVER, it seems to me that it's an all-or-nothing deal: in the
side-effects case, (1) should cause ctor(int) to be called, followed by
ctor(const MyClass&), since local "shall [not] be eliminated as an
optimization even if it appears to be unused" according to 3.7.2(3).
Conversely, in the no-side-effects case, there is no need to instantiate
local, and hence no need to evaluate the no-side-effects expression
MyLocal(1).

So, in the side-effects case, the output _must_ be:

MyClass(int=1)
MyClass(const MyClass &)
MyClass(int=2)
MyClass(const MyClass &)

whereas in the no-side-effects case, the output could quite validly (if
the compiler is maximally eliminating for optimization purposes) be nil.

Similar arguments apply to line (2), the initialization of local2 (see
comments on irrelevance of op= below).

> [...] what I was expecting:
>
> MyClass(int=1)
> MyClass(const MyClass &)
> MyClass(int=2)
> MyClass(const MyClass &)

> /* Cfont 4.0 on Sun produces:
>
> MyClass(int=1)
> MyClass(const MyClass &)
> MyClass(int=2)

> Another data point.  MSVC 4.2 produces:
>
> MyClass(int=1)
> MyClass(int=2)

IN SUMMARY:
----------
The output you were expecting would certainly be valid for a compliant
compiler in any case.

If MyClass::ctor(int) is deemed by the DIS to incur side effects
(something I'm not certain of -- can anyone else answer this?), then I
take issue with the compliance of both output samples you cite as having
come from real-world compilers. I do so according to a strict reading of
the DIS. In this case, the output you were expecting is the _only_ valid
output.

If, on the other hand, the DIS deems MyClass::ctor(int) to be
side-effect-free, then a compliant compiler would be at liberty to
optimize to the point where there would be no output for your code
example. Furthermore, in this case, both the cited real-world compilers
appear to be in compliance, though curiously, each is only performing
partial optimization.

>
> What's wrong, the compiler or my understanding of C++??
>

When you say, "I thought anything *I* explicitly specify must be
honored,"
you are, alas, mistaken.


FOOTNOTE: Irrelevance of op=
--------
The op= is irrelevant in this example because, as per 8.5 of the DIS,

   MyClass local2 = MyClass(2);

will (if not optimized away!) invoke the copy ctor, not op=. Here's the
germaine part of 8.5:

  The  form  of  initialization  (using  parentheses  or =) is generally
  insignificant, but does matter when the entity being initialized has a
  class  type ...

 --If the destination type is a (possibly cv-qualified) class type:

      [if] the initialization is direct-initialization, or if it is
copy-
      initialization where the cv-unqualified version of the source type
      is the same class as, or a derived class of, the class of the des-
      tination, constructors are considered.  The  applicable  construc-
      tors  are enumerated, and the best one is cho-
      sen through overload resolution.   The  constructor
      so  selected is called to initialize the object, with the initial-
      izer expression(s) as its argument(s).  If no constructor applies,
      or  the  overload  resolution  is ambiguous, the initialization is
      ill-formed.


-Will Hall
-----
willhall@chelsea.ios.com

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]






Author: mhcox@ptdprolog.net (Michael H. Cox)
Date: 1996/11/06
Raw View
Don't understand why the explicit call to constructor seems to be optimized
out. I understand
compilers are free to optimize out any intermediate temporaries their
initial code generation
schemes might produce, but I thought anything *I* explicitly specify must
be honored.

class MyClass {
public:
    MyClass(int);
    MyClass(const MyClass &rhs);
    MyClass & operator=(const MyClass& rhs);
};

#include <iostream.h>

MyClass::MyClass(int i)
{
    cerr << "MyClass(int" << "=" << i << ")" << endl;
}

MyClass::MyClass(const MyClass &rhs)
{
    cerr << "MyClass(const MyClass &)" << endl;
}

MyClass &MyClass::operator=(const MyClass& rhs)
{
    cerr << "MyClass& operator=(const MyClass& rhs)" << endl;
    return *this;

int
main(int, char**)
{
    MyClass local(MyClass(1));
    MyClass local2 = MyClass(2);

/* Cfont 4.0 on Sun produces:

MyClass(int=1)
MyClass(const MyClass &)
MyClass(int=2)

instead of what I was expecting:

MyClass(int=1)
MyClass(const MyClass &)
MyClass(int=2)
MyClass(const MyClass &)


What's wrong, the compiler or my understanding of C++??

P.S.
Another data point.  MSVC 4.2 produces:

MyClass(int=1)
MyClass(int=2)
*/
    return(0);
}

--
Michael H. Cox
mhcox@ptdprolog.net

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]