[dmd-internals] non-PODs again

Johannes Pfau johannespfau at googlemail.com
Fri Mar 8 04:40:36 PST 2013


Am 07.03.2013 22:19, schrieb Walter Bright:
> Oops, I missed that. It's a bug in dmd.
>
OK, this explains the inconsistency between normal and varargs functions.

>> nevertheless the non-POD is passed in registers. Add a 
>> __traits(isPOD, Date) test to the example, it returns false.
>>>
>>>>
>>>> I also don't understand how a copy ctor could break this.
>>>
>>> Because a copy ctor executes arbitrary code, and this just does not 
>>> work in the general case if a value is in a register.
>>
>> Yes, the struct value can't be passed _to the copy constructor_ in a 
>> register - but the copy ctor itself is always called with a reference 
>> to the value, i.e. it's declared as
>> __copyctor(ref Date this, ref Date b)
>>
>> For all other functions I don't see why it can't be passed in a 
>> register.
>>
>
> The copy constructor must have its address. Registers don't have an 
> address.
Yes, registers don't have an address. But think of passing in a 
registers as a "move", not as a copy and moving the value out of that 
register in the callee again:

TDPL, page 249:
"First off, D objects must be relocatable, that is location-independent: 
an object can be moved around memory be using raw memory move without 
it's integrity being affected. [...] It is illegal to create objects 
with internal pointers in D and the compiler and runtime subsystem are 
free to assume observance of this rule. [...] The postblit constructor 
makes copying equivalent to a move plus an optional user-defined hook 
[...]. The compiler is free to not insert the call to this(this) 
whenever it can prove the source of the copy will not be used after the 
copying process".

So C++ would probably pass a non-POD like this:

;-0x8(%ebp) is the local value
------------
mov -0x8(%ebp),-0x4(%ebp)                                ;create the 
copy for the callee function
lea    -0x4(%ebp),%eax                                         ;load 
address of copy
call   <_D1e4Date8__cpctorMxFKxS1e4DateZv>  ;call copy ctor for copy
lea    -0x4(%ebp),%eax                                         ;load 
address of copy
call <function> ;call function, passing a _reference_ to copy

<function>
(%eax)                           ;do something with object pointed to by eax
------------

And this is how it can be done when passing by value in a register

;-0x8(%ebp) is the local value
------------
mov -0x8(%ebp),-0x4(%ebp)                                ;create the 
copy for the callee function
lea    -0x4(%ebp),%eax                                         ;load 
address of copy
call   <_D1e4Date8__cpctorMxFKxS1e4DateZv>  ;call copy ctor for copy
mov    -0x4(%ebp),%eax                                      ;load 
_value_ of copy (move copy into eax) *
call <function> ;call function, passing a _value_

<function>
mov    %eax,-0x4(%ebp)                                      ;load the 
value passed in the register back onto stack
;(move copy from eax to stack) *
-0x4(%ebp) ;do something with object
------------

So why is the second example illegal? In the end in both examples we 
have a valid address as the value is moved out of the register again, so 
everything which can be done in the first example can be done in the 
second example. I only see 2 differences between those two examples:
* The second example has two additional memory moves (or bitblits). But 
it never accesses the original value again after a move, so according to 
TDPL this is legal and there is no need to call the copy ctor for these 
bit blits(moves).

* Because of the moves in the second example the address passed to the 
copy ctor call is different from the address <function> sees. This is 
why C++ can't do #2, as in C++ the address must be the same. TDPL 
explicitly states that moves and changing addresses is allowed in D, so 
this is not an issue.

So please explain what's illegal in this concrete example #2.

-- 
Johannes Pfau



More information about the dmd-internals mailing list