Article View: comp.std.c++
Article #2620Re: an optimization I claim is illegal
From: J. Kanze
Date: Fri, 17 Nov 1995 16:41
Date: Fri, 17 Nov 1995 16:41
122 lines
5684 bytes
5684 bytes
Alex Martelli (martelli@cadlab.it) wrote:
|> In a debate over on comp.compilers, there was some discussion of
|> whether it would be legal for a compiler to optimize the following
|> snippet of code...:
|> class P {
|> public: virtual void f();
|> // etc
|> };
|> // with a P* p
|> for(i=0;i<1000;i++)
|> p->f();
|> by hoisting out of the loop the virtual lookup for f()'s address.
|> I was astonished to see normally knowledgeable folks claim that
|> the optimization is legal, and the only counterexamples posted
|> involve illegal stuff such as F() memcpy()'ing all over "*this".
|> I tried to post what I believe is the correct way to show that
|> the optimization would be illegal, except basically when the
|> compiler knows enough about P and p that the virtual lookup
|> is not in fact needed, or when it knows everything about f()
|> and whatever it is that f() calls, etc -- but the c.c moderator
|> is apparently sick & tired of the issue, so I'm turning to here
|> for authoritative opinions... I think that if it is the committe's
|> desire to permit that optimization, then something presently
|> legal would be outlawed.
|> Here's the counterexample I have in mind:
[I've added line numbers for convenience in the discussion which
follows...]
|> 1 #include <iostream>
|> 3 class Base {
|> 4 public: virtual void f() {cout<<"Base ";}
|> 5 };
|> 6 class Derived: public Base {
|> 7 int countdown;
|> 8 public: Derived(int n=2):countdown(n) {}
|> 9 virtual void f();
|> 10 };
|> 11 int main()
|> 12 {
|> 13 Base *p=new Derived;
|> 14 for(int i=0;i<4;i++)
|> 15 p->f();
|> 16 cout<<endl;
|> 17 return 0;
|> 18 }
|> and, separately compiled perhaps,
|> 20 #include <iostream>
|> 21 #include <new>
|> 22 // above-seen declarations of Base and Derived, then:
|> 23 virtual void Derived::f() {
|> 24 cout<<"Derived ";
|> 25 if(--n==0) {
|> 26 this->~Derived();
|> 27 new(this) Base;
|> 28 }
|> 29 }
|> Isn't this a strictly conformant, well-defined program, and the
|> ONLY legal output it can give under a conformant compiler:
|> Derived Derived Base Base
|> ???
There's one obvious problem. The address of Base in Derived is not
guaranteed to be the same as the address of Derived. Thus, in line 13,
the actual address assigned to p will be that of the Base in Derived,
and *not* the address of the complete object. Calling throught the
virtual call mechanism will cause the address seen in functions of
Derived (including f) do be different that that in p.
Consider a trivial example: suppose an implementation puts the
additional data members of the derived class in front of the base class
at each derivation. In this case, the compiler will add the size of
these data members to the address returned by the new expression in line
13. The virtual function call mechanism will also arrange to subtract
this value from this address each time a virtual function resolves
dynamically to a function in Derived (typically by means of an offset in
the vtbl or a trampoline). The placement new in line 27 thus constructs
the Base lower in memory than it was previously, and lower in memory
than the address in p. *Any* use of p after this is undefined
(including further calls to f).
A general hint: when attempting to analyse such things, always use
multiple inheritance, with non-empty classes, and use the second base
specified. In this case, I think you would immediately see that it
cannot work; that there is no possible implementation for which it would
work, and thus, if it is guaranteed by the standard, the standard is in
error. Now note that there is *no* additional guarantee in the standard
concerning layout (or anything else) in the case of single inheritance;
if it doesn't work in some form of multiple inheritance (without virtual
base classes), it isn't guaranteed to work in single inheritance either.
I think it would be nice if language were added to the standard to
expressedly forbid this (rather than counting on the somewhat circuitous
argument above). However, such language must be thought out very
carefully in order not to break other things that should work. (I'm
thinking, in particular, of the assignment operator idiom that uses
explicit destruction and placement new. This works because the new type
is the same as the old.) So unless someone has the time to work out the
exact wording, I think we can live with having it implicitly illegal by
the above argument.
--
James Kanze (+33) 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, études et réalisations en logiciel orienté objet --
-- A la recherche d'une activité dans une region francophone
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Technical Details
Message-ID:
<48i55c$1tm@gabi.gabi-soft.fr>Path:
arc1-ca.pugleaf.net!engnews1.Eng.Sun.COM!taumet!clamageReferences:
<martelli.816557310@cadlab>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!engnews1.Eng.Sun.COM!taumet!clamage From: kanze@gabi-soft.fr (J. Kanze) Newsgroups: comp.std.c++ Subject: Re: an optimization I claim is illegal Date: 17 Nov 1995 16:41:24 GMT Organization: GABI Software, Sarl. Approved: clamage@eng.sun.com (comp.std.c++) Message-ID: <48i55c$1tm@gabi.gabi-soft.fr> References: <martelli.816557310@cadlab> NNTP-Posting-Host: taumet.eng.sun.com Content-Type: text X-Newsreader: TIN [version 1.2 PL2] Content-Length: 4828 X-Lines: 118 Originator: clamage@taumet