From: Martin B. on
Goran wrote:
> On Nov 24, 9:40 pm, George Neuner <gneun...(a)comcast.net> wrote:
>> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDCDC...(a)gmx.at>
>> wrote:
>>
>>> Hi all.
>>> ( Note that I've posted this to microsoft.public.vc.language a few days
>>> ago, but didn't really get an answer there, so I hope someone may shed
>>> some light on this in a more general context. )
>>> The Visual Studio compiler will never inline a funtion that returns an
>>> unwindable object (e.g. std::string, CString, etc.)
> (...)
> That said, could this have something to do with compiler not knowing
> the implementation of ~string, hence forcing a function call (not that
> I see a connection)? As you've shown, when destructor is visible to
> the compiler, all is indeed inline (but as usual with such examples,

As I have asserted in my other reply, it does not matter if the dtor is
visible. What was relevant in George's example was that MYPOINT did not
have a dtor. If you add a dtor that does anything, get() won't be inlined.

> one has to make sure that some unseen optimization doesn't play with
> "expected" results).
>

Yes. These examples can easily be compiled with only the /Ob2 or /Ob1
switch and all other optimizations disabled, which is what I'm doing.

cheers,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: George Neuner on
On Wed, 25 Nov 2009 08:19:39 CST, "Martin B." <0xCDCDCDCD(a)gmx.at>
wrote:

>George Neuner wrote:
>> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDCDCDCD(a)gmx.at>
>> wrote:
>
>> The first problem is, your code is not C++ but "managed C++", ...
>
>Incorrect. The security_cookie is generated by the /GS option (see:
>http://msdn.microsoft.com/en-us/library/8dbf701c%28VS.80%29.aspx) adding
>/GS- to the commandline will remove these references from the disassembly.

My bad. I blanked on buffer check because I've been doing a lot of
managed code recently and got used to seeing checks in disassemblies.


>I have checked and your example does *not* contain an *unwindable*
>object. MYPOINT has an empty dtor, because it contains only int members.
>Adding a non-empty dtor to mypoint, for example:
> MYPOINT::~MYPOINT() {
> cout << "This is a non-empty dtor\n";
> }
>will effectively prevent MYPOINT get() from being inlined.

Miscommunication I think. I generally associate "unwind(able)" with
use of exceptions, not just having a dtor. Since string and stream (as
above) operations can throw and it's documented that throw code won't
be inlined, I created code that didn't.

Anyway, you are right that having a non-trivial dtor makes VC++ call
the value object returning function rather than inlining it. Of
course the obvious fix is (if possible) to return a reference to the
object instead.

I changed the code as follows to have a non-trivial dtor:

static int constructs = 0;
static int destructs = 0;

class MYPOINT
{
public:
int x, y, z;
public:
MYPOINT( int i, int j, int k )
{
x = i; y = j; z = k;
++constructs;
};

MYPOINT( MYPOINT& src )
{
x = src.x; y = src.y; z = src.z;
++constructs;
}

~MYPOINT()
{
++destructs;
}
};

class Simple
{
public:
MYPOINT s_;

public:
Simple()
: s_(3,4,7)
{ }

MYPOINT get()
{
return s_;
}
};

void testsimple()
{
Simple oSimple;
MYPOINT s1( oSimple.get() );
cout << s1.z << endl;
}


VC++ compiles the explicit call to get() as you expected. However,
GCC4 (MinGW) refuses to compile this code:

test.cpp: In function 'void testsimple()':
test.cpp:61: error: no matching function for call to
'MYPOINT::MYPOINT(MYPOINT)'

and so does VC++ if you disable MS's language extensions:

Error 1 error C2558: class 'MYPOINT' : no copy constructor
available or copy constructor is declared 'explicit'
test.cpp 61 test

If you change get() to return MYPOINT&, then both compilers will
inline the get() call in testsimple() regardless of the dtor.


As for why the nontrivial dtor prevents inlining functions that value
return objects, I can only speculate ... I haven't had occasion to
look under the hood of a C++ compiler in quite a few years.

My best guess is that it's too much trouble - in general - for the
compiler to decide whether the temporary return object can be renamed
and used directly or will need to be destructed in the caller - so
there is a blanket edict against inlining such functions.

The experts in comp.compilers are likely to know more (or at least
guess better).

George

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Martin B. on
George Neuner wrote:
> On Wed, 25 Nov 2009 08:19:39 CST, "Martin B." <0xCDCDCDCD(a)gmx.at>
> wrote:
>> I have checked and your example does *not* contain an *unwindable*
>> object. MYPOINT has an empty dtor, because it contains only int members.
>> Adding a non-empty dtor to mypoint, for example:
>> MYPOINT::~MYPOINT() {
>> cout << "This is a non-empty dtor\n";
>> }
>> will effectively prevent MYPOINT get() from being inlined.
>
> Miscommunication I think. I generally associate "unwind(able)" with
> use of exceptions, not just having a dtor. Since string and stream (as
> above) operations can throw and it's documented that throw code won't
> be inlined, I created code that didn't.
>

It has nothing to do with throw but with catch.
A function such as
inline int get_throwing_x() const {
if(x > 0)
return x;
else
throw std::runtime_error("x is not > 0!");
}
will be inlined allright.

What will be *not* inlined is a function containing a try:
inline int get_try_x() const {
try {
s.empty();
return x-1;
} catch(std::exception const& e) {
return x+strlen(e.what());
}
}
.... will not be inlined. (see:
http://msdn.microsoft.com/en-us/library/a98sb923.aspx)

> Anyway, you are right that having a non-trivial dtor makes VC++ call
> the value object returning function rather than inlining it. Of
> course the obvious fix is (if possible) to return a reference to the
> object instead.
> (...)
>

That's why I provided a sample with a string return value. You should
not return a reference to the objects internals.

>
> As for why the nontrivial dtor prevents inlining functions that value
> return objects, I can only speculate ... I haven't had occasion to
> look under the hood of a C++ compiler in quite a few years.
>
> My best guess is that it's too much trouble - in general - for the
> compiler to decide whether the temporary return object can be renamed
> and used directly or will need to be destructed in the caller - so
> there is a blanket edict against inlining such functions.
>

Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
object for the string object, even if all optimizations are turned off.
So I can't imagine how the temporary could pose a problem here.

> The experts in comp.compilers are likely to know more (or at least
> guess better).
>

Thanks.
I guess it's off to a third NG with the question :-)


cheers,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: George Neuner on
On Sat, 28 Nov 2009 02:39:57 CST, "Martin B." <0xCDCDCDCD(a)gmx.at>
wrote:

>George Neuner wrote:
>
>> My best guess is that it's too much trouble - in general - for the
>> compiler to decide whether the temporary return object can be renamed
>> and used directly or will need to be destructed in the caller - so
>> there is a blanket edict against inlining such functions.
>
>Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
>object for the string object, even if all optimizations are turned off.
>So I can't imagine how the temporary could pose a problem here.

The compiler always _tries_ to do RVO, but there are plenty of cases
where it is not possible - one of which is passing an object by value
where a reference is expected (a Microsoft extension).

If you do something like:

string G() { ... }
string F( string &in ) { string out; ...; return out; }

string x = F( G() );

you'll find that the return value from F() is optimized as expected
but for F(G()) the return value from G() is a hidden stack temporary,
a reference to which is then passed to F().

George

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Martin B. on
George Neuner wrote:
> On Sat, 28 Nov 2009 02:39:57 CST, "Martin B." <0xCDCDCDCD(a)gmx.at>
> wrote:
>
>> George Neuner wrote:
>>
>>> My best guess is that it's too much trouble - in general - for the
>>> compiler to decide whether the temporary return object can be renamed
>>> and used directly or will need to be destructed in the caller - so
>>> there is a blanket edict against inlining such functions.
>> Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
>> object for the string object, even if all optimizations are turned off.
>> So I can't imagine how the temporary could pose a problem here.
>
> The compiler always _tries_ to do RVO, but there are plenty of cases
> where it is not possible - one of which is passing an object by value
> where a reference is expected (a Microsoft extension).
>
> If you do something like:
>
> string G() { ... }
> string F( string &in ) { string out; ...; return out; }
>
> string x = F( G() );
>
> you'll find that the return value from F() is optimized as expected
> but for F(G()) the return value from G() is a hidden stack temporary,
> a reference to which is then passed to F().
>

Cheers, that's an interesting point.
One might argue though that the hidden stack temporary "belongs" to the
F(.) call and thus does say nothing about RVO. :-)
That is, given:

string G() { string s; ... returns s; }
string F(string &in) { string out; ... returns out; }

and given two different code versions:

void t3() { // uses MS extension
string s;
s = F( G() );
}

void t4() {
string s;
string tmp(G());
s = F( tmp );
}

this will essentially generate the same assembly for t3 and t4 so I'm
really not sure if we can and should say that "RVO does not happen".

br,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]