Jump to page: 1 2
Thread overview
null Vs [] return arrays
Mar 27, 2011
bearophile
Mar 27, 2011
Kagamin
Mar 27, 2011
bearophile
Mar 27, 2011
Jonathan M Davis
Mar 27, 2011
bearophile
Mar 28, 2011
Kagamin
Mar 28, 2011
bearophile
Apr 01, 2011
Regan Heath
Apr 01, 2011
bearophile
Apr 01, 2011
Torarin
Apr 01, 2011
spir
Apr 01, 2011
Regan Heath
Apr 05, 2011
Regan Heath
Apr 07, 2011
Regan Heath
March 27, 2011
I have compiled this little D2 program:


int[] foo() {
    return [];
}
int[] bar() {
    return null;
}
void main() {}



Using DMD 2.052,  dmd -O -release -inline test2.d

This is the asm of the two functions:

_D5test23fooFZAi    comdat
L0:     push    EAX
        mov EAX,offset FLAT:_D11TypeInfo_Ai6__initZ
        push    0
        push    EAX
        call    near ptr __d_arrayliteralT
        mov EDX,EAX
        add ESP,8
        pop ECX
        xor EAX,EAX
        ret

_D5test23barFZAi    comdat
        xor EAX,EAX
        xor EDX,EDX
        ret

Is this expected and desired? Isn't it better to compile the foo() as bar()?

Bye,
bearophile
March 27, 2011
bearophile Wrote:

> I have compiled this little D2 program:
> 
> 
> int[] foo() {
>     return [];
> }
> int[] bar() {
>     return null;
> }
> void main() {}
> 
> 
> 
> Using DMD 2.052,  dmd -O -release -inline test2.d
> 
> This is the asm of the two functions:
> 
> _D5test23fooFZAi    comdat
> L0:     push    EAX
>         mov EAX,offset FLAT:_D11TypeInfo_Ai6__initZ
>         push    0
>         push    EAX
>         call    near ptr __d_arrayliteralT
>         mov EDX,EAX
>         add ESP,8
>         pop ECX
>         xor EAX,EAX
>         ret
> 
> _D5test23barFZAi    comdat
>         xor EAX,EAX
>         xor EDX,EDX
>         ret
> 
> Is this expected and desired? Isn't it better to compile the foo() as bar()?
> 
> Bye,
> bearophile

[] is not null, it's an array of 0 elements, what is done exactly. edx points to the allocated array.
March 27, 2011
Kagamin:

> [] is not null, it's an array of 0 elements, what is done exactly. edx points to the allocated array.

I don't understand what you say. I think the caller of foo() and bar() receive the same thing, two empty registers. I think that cast(int[])null and cast(int[])[] are the same thing for D.

void main() {
    assert(cast(int[])null == cast(int[])null);
    auto a1 = cast(int[])null;
    a1 ~= 1;
    auto a2 = 1 ~ cast(int[])null;
}

Bye,
bearophile
March 27, 2011
On 2011-03-27 11:42, bearophile wrote:
> Kagamin:
> > [] is not null, it's an array of 0 elements, what is done exactly. edx points to the allocated array.
> 
> I don't understand what you say. I think the caller of foo() and bar()
> receive the same thing, two empty registers. I think that cast(int[])null
> and cast(int[])[] are the same thing for D.
> 
> void main() {
>     assert(cast(int[])null == cast(int[])null);
>     auto a1 = cast(int[])null;
>     a1 ~= 1;
>     auto a2 = 1 ~ cast(int[])null;
> }

What I would _expect_ the difference between a null array and an empty one to be would be that the null one's ptr property would be null, whereas the empty one wouldn't be. But dmd treats them pretty much the same. empty returns true for both. You can append to both. The null one would be a guaranteed memory reallocation when you append to it whereas the empty one may not be, but their behavior is almost identical.

How that affects the generated assembly code, I don't know. Particularly if you're compiling with -inline and and -O, the compiler can likely make assumptions about null that it can't make about [], since it probably treats [] more generally without worrying about the fact that it happens to be empty as far as optimizations go - that and there _is_ a semantic difference between null and [] if you're messing with the ptr property, so Walter may think that it's best for null to not be turned into the same thing as [] automatically.

- Jonathan M Davis
March 27, 2011
Jonathan M Davis:

> the compiler can likely make assumptions about null that it can't make about [], since it probably treats [] more generally without worrying about the fact that it happens to be empty as far as optimizations go - that and there _is_ a semantic difference between null and [] if you're messing with the ptr property, so Walter may think that it's best for null to not be turned into the same thing as [] automatically.

Thank you for your answer. I have added a low-priority enhancement request.

Bye,
bearophile
March 28, 2011
bearophile Wrote:

> Kagamin:
> 
> > [] is not null, it's an array of 0 elements, what is done exactly. edx points to the allocated array.
> 
> I don't understand what you say. I think the caller of foo() and bar() receive the same thing, two empty registers. I think that cast(int[])null and cast(int[])[] are the same thing for D.

That's a mistake.

Well, if there's no differnce for you, you can use either of them. What's the problem?
March 28, 2011
On Sun, 27 Mar 2011 09:37:47 -0400, bearophile <bearophileHUGS@lycos.com> wrote:

> I have compiled this little D2 program:
>
>
> int[] foo() {
>     return [];
> }
> int[] bar() {
>     return null;
> }
> void main() {}
>
>
>
> Using DMD 2.052,  dmd -O -release -inline test2.d
>
> This is the asm of the two functions:
>
> _D5test23fooFZAi    comdat
> L0:     push    EAX
>         mov EAX,offset FLAT:_D11TypeInfo_Ai6__initZ
>         push    0
>         push    EAX
>         call    near ptr __d_arrayliteralT
>         mov EDX,EAX
>         add ESP,8
>         pop ECX
>         xor EAX,EAX
>         ret
>
> _D5test23barFZAi    comdat
>         xor EAX,EAX
>         xor EDX,EDX
>         ret
>
> Is this expected and desired? Isn't it better to compile the foo() as bar()?

Probably.  The runtime that allocates an array looks like this (irrelevant parts collapsed):


extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
{
    auto sizeelem = ti.next.tsize();            // array element size
    void* result;

    ...
    if (length == 0 || sizeelem == 0)
        result = null;
    else
    {
       ...
    }
    return result;
}

So essentially, you are getting the same thing, but using [] is slower.

-Steve
March 28, 2011
Steven Schveighoffer:

> So essentially, you are getting the same thing, but using [] is slower.

It seems I was right then, thank you and Kagamin for the answers.

Bye,
bearophile
April 01, 2011
On Mon, 28 Mar 2011 17:54:29 +0100, bearophile <bearophileHUGS@lycps.com> wrote:
> Steven Schveighoffer:
>
>> So essentially, you are getting the same thing, but using [] is slower.
>
> It seems I was right then, thank you and Kagamin for the answers.

This may be slightly OT but I just wanted to raise the point that conceptually it's nice to be able to express (exists but is empty) and (does not exist).  Pointers/references have null as a (does not exist) "value" and this is incredibly useful.  Try doing the same thing with 'int' .. it requires you either use int* or pass an additional boolean to indicate existence.. yuck.

I'd suggest if someone types '[]' they mean (exists but is empty) and if they type 'null' they mean (does not exist) and they may be relying on the .ptr value to differentiate these cases, which is useful.  If you're not interested in the difference, and you need performance, you simply use 'null'.  Everybody is happy. :)

R
April 01, 2011
Regan Heath:

> conceptually it's nice to be able to express (exists but is empty) and (does not exist).

You may want to express that, but for the implementation of the language those two situations are the same, because in the [] literal the ptr is null. So I think it's better for the programmer to not differentiate the two situations, because they are not different. If the programmer tells them apart, he/she is doing something bad in D, creating a false illusion.

Bye,
bearophile
« First   ‹ Prev
1 2