[cxx-abi-dev] Transfer modes for parameters and return values
David Vandevoorde
daveed at edg.com
Thu Jun 4 15:05:49 UTC 2009
On Jun 4, 2009, at 2:07 AM, Lawrence Crowl wrote:
> On Wed, Jun 3, 2009 at 4:59 PM, David Vandevoorde <daveed at edg.com>
> wrote:
>> On Jun 3, 2009, at 5:23 PM, Lawrence Crowl wrote:
>>> On 6/3/09, David Vandevoorde <daveed at edg.com> wrote:
>>>>
>>>> Hello again,
>>>>
>>>> In 3.1.1 and 3.1.4, the ABI specifies that "by value" class type
>>>> parameters
>>>> and class type return values are passed via the address of a
>>>> reference if
>>>> the class type has
>>>> (a) a nontrivial destructor, or
>>>> (b) a nontrivial copy constructor.
>>>>
>>>> Should we now also add to that:
>>>> (c) a (nontrivial) move constructor
>>>> ?
>>>>
>>>> (There is currently no notion of "trivial move constructor", but
>>>> I think
>>>> there are suggestions to introduce that in the future.)
>>>
>>> I'm not sure I can predict the binary consequences of such a notion
>>> until it is actually defined. So, I would rather wait until then.
>>
>> It doesn't really have to be defined formally: A constructor for
>> class X
>> whose first parameter has type
>> X cv-quals&&
>> and whose other parameters have a default argument or are the
>> ellipsis
>> parameter. Such a constructor can be preferred over the trivial copy
>> constructor for "rvalue copying" (i.e., moving) purposes.
>
> I think I am confused. I have two comments, and I don't know which
> applies.
>
> Since we don't know what a 'trivial' move constructor is, how do we
> know
> how it might be implemented, and therefore whether it affects the ABI?
>
> The choice of constructor isn't part of the ABI, but of the
> semantics of
> the program. Once the semantics are picked, the ABI keeps it
> stable, so
> I don't see how any preference for the move constructor would require
> us to modify how we represent parameters for copy construction.
Let me address the second comment first. The language rules have a
consequence for the ABI. A user-provided move constructor can do
things with the "this" pointer, and that means that the object moved
to (the one pointed to by "this") must be in memory, not in
registers. However, right now, the criterion for passing the argument
in memory instead of (potentially) in registers, is based solely on
the nature of the destructor and copy constructors.
Regarding your first comment, I'm postulating that if "trivial move
constructor" were introduced it would imply that such a constructor
could be implemented as a bitwise move (i.e., via register transfer).
Note that if I'm correct, there is already a bug in the ABI because a
class like:
struct S {
S(int);
};
void f(S);
today is passed "by trivial copy" in the function f (according to the
ABI). However, my understanding is that in
int main() {
f(3);
}
no copy constructor should be called (even "conceptually") for the
argument transfer (i.e., despite it being a "copy initialization" it
behaves like a "direct initialization). Unfortunately, that's not
possible with the ABI. In practice, I think all implementations
instead do the following:
- initialize a temporary with "S tmp(3);"
- bitwise copy the temporary to the parameter variable of f
Now consider the following completion of the code above:
#include <assert.h>
S *p;
S::S(int) { p = this; }
void f(S x) {
assert(&x == p);
}
I _think_ that program is supposed to terminate normally (according to
the language rules), but with real implementations the assertion fails.
We cannot fix the ABI if that's indeed a bug, but we can avoid it for
classes with user-provided "move constructors" (since they didn't
previously exist). Also, I have this vague sense that it is more
important for such cases: Ordinarily, one doesn't play "this-pointer
games" such as those in the program above with converting
constructors, but with move constructors perhaps that might be more
frequent.
Daveed
More information about the cxx-abi-dev
mailing list