Jump to page: 1 2
Thread overview
[dmd-internals] non-PODs again
Mar 07, 2013
Johannes Pfau
Mar 07, 2013
Walter Bright
Mar 07, 2013
Johannes Pfau
Mar 07, 2013
Walter Bright
Mar 08, 2013
Maxim Fomin
Mar 08, 2013
Johannes Pfau
Mar 08, 2013
Maxim Fomin
Mar 08, 2013
Johannes Pfau
Mar 08, 2013
Johannes Pfau
Mar 08, 2013
Walter Bright
Mar 09, 2013
Johannes Pfau
Mar 10, 2013
Walter Bright
Mar 12, 2013
Johannes Pfau
March 07, 2013
I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.

So from last discussion:
>> Wouldn't it be legal to still pass non-PODs in registers when calling functions and only copying them back to
>> the stack if the address is needed? As we pass structs by value anyway, how could this be problematic?
>
> No, not allowed. Consider why there are copy constructors, and what they do.

I compiled some test programs with dmd and dmd _does_ pass non-POD values in registers as I suggested above.
See this example:
https://gist.github.com/jpf91/5064703 (D)
https://gist.github.com/jpf91/5064764 (ASM)

I also don't understand how a copy ctor could break this. I asked on D.learn and there was no answer. Of course in C++ where structs have identity and interior pointers are allowed passing in register could be bad as it changes the address. But in D this is explicitly allowed. So I take it that this is actually allowed?

What is weird then is that dmd passes non-PODs in registers for normal functions, but for varargs functions they're always passed on the stack. I don't think there's a reason to make this difference. I know the SysV ABI specifies that non-PODs are never passed in registers, but that's only true for C++ PODs*, I don't  see why D non-PODs (no identity, internal pointers) couldn't be passed in registers for varargs functions as well.


DMD also uses the "invisible reference" trick described in the SysV ABI for all non-PODs. But again, I don't see why returning in a register would be problematic. In pseudo-asm:

What we have now in DMD:
--------
lea -0x4(%ebp),%edi
call <function>

<function>:
mov <returnvalue>, (%edi)
ret
--------

Why would returning in a register be illegal?
Especially considering that we pass parameters in registers, why not do the same for return values?
--------
call <function>
mov %eax, -0x4(%ebp)

<function>:
mov <returnvalue>, %eax
ret
--------


> Whenever you make a copy of a non-POD value, you have to (for example) build an exception handling frame to destruct it if, in
> the meantime, an exception is thrown. This is not only expensive, but it doesn't work if the value lives in registers.

But if the copy is only a temporary bitcopy, i.e the copy constructor & postblit wasn't called (and this should be valid as D structs don't have identity/internal pointers) then there's no need to call the destructor? Maybe I should rather talk about "moving" than copying as in C++s move semantics.



* There's actually an explanation in the ABI pdf:
--------
A non-POD object cannot be passed in registers because such objects must have well defined
addresses; the address at which an object is constructed (by the caller) and the address at which
the object is destroyed (by the callee) must be the same. Similar issues apply when returning a
non-POD object from a function.
--------
However, with the current DMD implementation this is not the case. There is also no documentation stating that D non-PODs are constructed/destructed at the same address. http://dlang.org/struct.html says "A struct is defined to _not have an identity_; that is, the implementation is free to make bit copies of the struct as convenient."

-- 
Johannes Pfau

_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

March 07, 2013
On 3/7/2013 9:36 AM, Johannes Pfau wrote:
> I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.
>
> So from last discussion:
> >> Wouldn't it be legal to still pass non-PODs in registers when calling 
> functions and only copying them back to
> >> the stack if the address is needed? As we pass structs by value anyway, how 
> could this be problematic?
> >
> > No, not allowed. Consider why there are copy constructors, and what they do.
>
> I compiled some test programs with dmd and dmd _does_ pass non-POD values in registers as I suggested above.
> See this example:
> https://gist.github.com/jpf91/5064703 (D)
> https://gist.github.com/jpf91/5064764 (ASM)

That's because objects with constructors are now regarded as POD.

>
> 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.

_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

March 07, 2013
Am 07.03.2013 20:45, schrieb Walter Bright:
>
> On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>> I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.
>>
>> So from last discussion:
>> >> Wouldn't it be legal to still pass non-PODs in registers when 
>> calling functions and only copying them back to
>> >> the stack if the address is needed? As we pass structs by value 
>> anyway, how could this be problematic?
>> >
>> > No, not allowed. Consider why there are copy constructors, and what 
>> they do.
>>
>> I compiled some test programs with dmd and dmd _does_ pass non-POD values in registers as I suggested above.
>> See this example:
>> https://gist.github.com/jpf91/5064703 (D)
>> https://gist.github.com/jpf91/5064764 (ASM)
>
> That's because objects with constructors are now regarded as POD.

This example uses a postblit to make sure the type is not a POD. It's obvious in the ASM that the copy ctor is called, 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.

-- 
Johannes Pfau

_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

March 07, 2013
On 3/7/2013 12:19 PM, Johannes Pfau wrote:
> Am 07.03.2013 20:45, schrieb Walter Bright:
>>
>> On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>>> I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.
>>>
>>> So from last discussion:
>>> >> Wouldn't it be legal to still pass non-PODs in registers when calling 
>>> functions and only copying them back to
>>> >> the stack if the address is needed? As we pass structs by value anyway, 
>>> how could this be problematic?
>>> >
>>> > No, not allowed. Consider why there are copy constructors, and what they do.
>>>
>>> I compiled some test programs with dmd and dmd _does_ pass non-POD values in registers as I suggested above.
>>> See this example:
>>> https://gist.github.com/jpf91/5064703 (D)
>>> https://gist.github.com/jpf91/5064764 (ASM)
>>
>> That's because objects with constructors are now regarded as POD.
>
> This example uses a postblit to make sure the type is not a POD. It's obvious in the ASM that the copy ctor is called,

Oops, I missed that. It's a bug in dmd.

> 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.
_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

March 08, 2013
2013/3/8 Walter Bright <walter@digitalmars.com>

>
> On 3/7/2013 12:19 PM, Johannes Pfau wrote:
>
>> Am 07.03.2013 20:45, schrieb Walter Bright:
>>
>>>
>>> On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>>>
>>>> I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.
>>>>
>>>> So from last discussion:
>>>> >> Wouldn't it be legal to still pass non-PODs in registers when
>>>> calling functions and only copying them back to
>>>> >> the stack if the address is needed? As we pass structs by value
>>>> anyway, how could this be problematic?
>>>> >
>>>> > No, not allowed. Consider why there are copy constructors, and what
>>>> they do.
>>>>
>>>> I compiled some test programs with dmd and dmd _does_ pass non-POD
>>>> values in registers as I suggested above.
>>>> See this example:
>>>> https://gist.github.com/jpf91/**5064703<https://gist.github.com/jpf91/5064703>(D)
>>>> https://gist.github.com/jpf91/**5064764<https://gist.github.com/jpf91/5064764>(ASM)
>>>>
>>>
>>> That's because objects with constructors are now regarded as POD.
>>>
>>
>> This example uses a postblit to make sure the type is not a POD. It's obvious in the ASM that the copy ctor is called,
>>
>
> Oops, I missed that. It's a bug in dmd.
>

Isn't there another bug with struct parameter which is copied twice - on caller and callee side?

function  D main
Date d = _D1e4Date6__initZ;
setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))

function  e.setDate
x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))


March 08, 2013
Am 08.03.2013 05:52, schrieb Maxim Fomin:
>
>
> 2013/3/8 Walter Bright <walter@digitalmars.com <mailto:walter@digitalmars.com>>
>
>
>     On 3/7/2013 12:19 PM, Johannes Pfau wrote:
>
>         Am 07.03.2013 20:45, schrieb Walter Bright:
>
>
>             On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>
>                 I'm sorry I have to pester you with this again, but I
>                 still have some questions regarding POD types and I'd
>                 like to fix this in GDC.
>
>                 So from last discussion:
>                 >> Wouldn't it be legal to still pass non-PODs in
>                 registers when calling functions and only copying them
>                 back to
>                 >> the stack if the address is needed? As we pass
>                 structs by value anyway, how could this be problematic?
>                 >
>                 > No, not allowed. Consider why there are copy
>                 constructors, and what they do.
>
>                 I compiled some test programs with dmd and dmd _does_
>                 pass non-POD values in registers as I suggested above.
>                 See this example:
>                 https://gist.github.com/jpf91/5064703 (D)
>                 https://gist.github.com/jpf91/5064764 (ASM)
>
>
>             That's because objects with constructors are now regarded
>             as POD.
>
>
>         This example uses a postblit to make sure the type is not a
>         POD. It's obvious in the ASM that the copy ctor is called,
>
>
>     Oops, I missed that. It's a bug in dmd.
>
>
> Isn't there another bug with struct parameter which is copied twice - on caller and callee side?
>
> function  D main
> Date d = _D1e4Date6__initZ;
> setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))
>
> function  e.setDate
> x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))
>
setDate assigns d to the global variable x so the second call to the cpctor seems to be caused by that and valid.

-- 
Johannes Pfau



March 08, 2013
2013/3/8 Johannes Pfau <johannespfau@googlemail.com>

>  Am 08.03.2013 05:52, schrieb Maxim Fomin:
>
>
>
> 2013/3/8 Walter Bright <walter@digitalmars.com>
>
>>
>> On 3/7/2013 12:19 PM, Johannes Pfau wrote:
>>
>>> Am 07.03.2013 20:45, schrieb Walter Bright:
>>>
>>>>
>>>> On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>>>>
>>>>> I'm sorry I have to pester you with this again, but I still have some questions regarding POD types and I'd like to fix this in GDC.
>>>>>
>>>>> So from last discussion:
>>>>> >> Wouldn't it be legal to still pass non-PODs in registers when
>>>>> calling functions and only copying them back to
>>>>> >> the stack if the address is needed? As we pass structs by value
>>>>> anyway, how could this be problematic?
>>>>> >
>>>>> > No, not allowed. Consider why there are copy constructors, and what
>>>>> they do.
>>>>>
>>>>> I compiled some test programs with dmd and dmd _does_ pass non-POD
>>>>> values in registers as I suggested above.
>>>>> See this example:
>>>>> https://gist.github.com/jpf91/5064703 (D)
>>>>> https://gist.github.com/jpf91/5064764 (ASM)
>>>>>
>>>>
>>>> That's because objects with constructors are now regarded as POD.
>>>>
>>>
>>> This example uses a postblit to make sure the type is not a POD. It's obvious in the ASM that the copy ctor is called,
>>>
>>
>>  Oops, I missed that. It's a bug in dmd.
>>
>
> Isn't there another bug with struct parameter which is copied twice - on caller and callee side?
>
> function  D main
> Date d = _D1e4Date6__initZ;
> setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))
>
> function  e.setDate
> x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))
>
>  setDate assigns d to the global variable x so the second call to the
> cpctor seems to be caused by that and valid.
>
> --
> Johannes Pfau
>
>
> DMD still generates double copy if variable is changed to local

void setDate(Date d)
{
Date x;
x = d;
}

function  e.setDate
Date x = _D1e4Date6__initZ;
x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))

function  D main
Date d = _D1e4Date6__initZ;
setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))

Anyway whether variable is tls or not is irrelevant. Compiler should not make a copy when assigning.


March 08, 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

_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

March 08, 2013
Am 08.03.2013 13:15, schrieb Maxim Fomin:
>
>
> 2013/3/8 Johannes Pfau <johannespfau@googlemail.com <mailto:johannespfau@googlemail.com>>
>
>     Am 08.03.2013 05:52, schrieb Maxim Fomin:
>>
>>
>>     2013/3/8 Walter Bright <walter@digitalmars.com
>>     <mailto:walter@digitalmars.com>>
>>
>>
>>         On 3/7/2013 12:19 PM, Johannes Pfau wrote:
>>
>>             Am 07.03.2013 20:45, schrieb Walter Bright:
>>
>>
>>                 On 3/7/2013 9:36 AM, Johannes Pfau wrote:
>>
>>                     I'm sorry I have to pester you with this again,
>>                     but I still have some questions regarding POD
>>                     types and I'd like to fix this in GDC.
>>
>>                     So from last discussion:
>>                     >> Wouldn't it be legal to still pass non-PODs in
>>                     registers when calling functions and only copying
>>                     them back to
>>                     >> the stack if the address is needed? As we pass
>>                     structs by value anyway, how could this be
>>                     problematic?
>>                     >
>>                     > No, not allowed. Consider why there are copy
>>                     constructors, and what they do.
>>
>>                     I compiled some test programs with dmd and dmd
>>                     _does_ pass non-POD values in registers as I
>>                     suggested above.
>>                     See this example:
>>                     https://gist.github.com/jpf91/5064703 (D)
>>                     https://gist.github.com/jpf91/5064764 (ASM)
>>
>>
>>                 That's because objects with constructors are now
>>                 regarded as POD.
>>
>>
>>             This example uses a postblit to make sure the type is not
>>             a POD. It's obvious in the ASM that the copy ctor is called,
>>
>>
>>         Oops, I missed that. It's a bug in dmd.
>>
>>
>>     Isn't there another bug with struct parameter which is copied
>>     twice - on caller and callee side?
>>
>>     function  D main
>>     Date d = _D1e4Date6__initZ;
>>     setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))
>>
>>     function  e.setDate
>>     x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))
>>
>     setDate assigns d to the global variable x so the second call to
>     the cpctor seems to be caused by that and valid.
>
>     -- 
>     Johannes Pfau
>
>
> DMD still generates double copy if variable is changed to local
>
> void setDate(Date d)
> {
> Date x;
> x = d;
> }
>
> function  e.setDate
> Date x = _D1e4Date6__initZ;
> x.opAssign((Date __cpcttmp6 = __cpcttmp6.__cpctor(d); , __cpcttmp6))
>
> function  D main
> Date d = _D1e4Date6__initZ;
> setDate((Date __cpcttmp7 = __cpcttmp7.__cpctor(d); , __cpcttmp7))
>
> Anyway whether variable is tls or not is irrelevant. Compiler should not make a copy when assigning.
>
You're right I confused this with initialization which needs the call to the copy ctor, but a simple assignment should not call it.

-- 
Johannes Pfau



March 08, 2013
On 3/8/2013 4:40 AM, Johannes Pfau wrote:
>
> So please explain what's illegal in this concrete example #2.

If it lives in a register, then exception handling recovery won't work on it.

_______________________________________________
dmd-internals mailing list
dmd-internals@puremagic.com
http://lists.puremagic.com/mailman/listinfo/dmd-internals

« First   ‹ Prev
1 2