Jump to page: 1 24  
Page
Thread overview
Multiple returns / was: x, y, z = 0
May 29, 2002
Christian Schüler
May 29, 2002
Pavel Minayev
May 29, 2002
Russ Lewis
May 29, 2002
Christian Schüler
May 30, 2002
Pavel Minayev
May 30, 2002
anderson
May 30, 2002
Pavel Minayev
Jun 01, 2002
anderson
Jun 01, 2002
Pavel Minayev
Jun 01, 2002
anderson
Jun 01, 2002
Pavel Minayev
Jun 02, 2002
anderson
Jun 02, 2002
anderson
Jun 02, 2002
Pavel Minayev
Jun 03, 2002
anderson
Jun 03, 2002
anderson
Jun 03, 2002
Pavel Minayev
Jun 03, 2002
anderson
overloading...
Jun 03, 2002
Pavel Minayev
Jun 03, 2002
anderson
Jun 03, 2002
anderson
Jun 03, 2002
Pavel Minayev
Jun 04, 2002
anderson
Jun 05, 2002
anderson
Jun 03, 2002
Sean L. Palmer
Jun 03, 2002
anderson
Jun 03, 2002
anderson
Jun 12, 2002
Walter
Jun 13, 2002
anderson
Jun 13, 2002
Walter
Jun 13, 2002
anderson
May 30, 2002
Karl Bochert
May 30, 2002
Pavel Minayev
May 30, 2002
Christian Schüler
May 31, 2002
Pavel Minayev
May 30, 2002
Martin M. Pedersen
May 30, 2002
Pavel Minayev
Jun 14, 2002
Walter
May 29, 2002
I'm moving this discussion to a new thread.
What about multiple return values, like in LUA:

--- LUA code begin ---

function foo()
    return 1, 2, 3
end

a, b, c = foo()

function bar( x, y, z )
    return x + y + z
end

sum = bar( foo() )

--- LUA code end ---







May 29, 2002
"Christian Schüler" <cschueler@gmx.de> wrote in message news:ad36rt$16n9$1@digitaldaemon.com...

> I'm moving this discussion to a new thread.
> What about multiple return values, like in LUA:

And how do you want this to be implemented? Lua is a bytecode interpreter, it has its own stack-based VM, and return values are passed on the stack... it's all easily handled. But I don't think you can easily do it in a language which compiles to native code.

BTW, out-parameters are supposed to exist for such cases =)


May 29, 2002
Pavel Minayev wrote:

> BTW, out-parameters are supposed to exist for such cases =)

I keep forgetting about those!

Hey Walter, is there any way to omit an out parameter?  Of course, inout parameters are required...but there any way to skip an out parameter? I'd hate to make a temp variable whenever there was an out parameter I didn't use.  This would probably require the compiler to declare a temporary for you on the stack, and pass that as the out parameter...but better the compiler than me, I always say!  :)

--
The Villagers are Online! villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]


May 29, 2002
> But I don't
> think you can easily do it in a language which compiles to
> native code.

Yes, that's the point. I was thinking about it how such a feature could be implemented. Let's suppose you can declare a function to have multiple returns in a compiled language (like C or D):

int, int, int foo()
{
    return 1, 2, 3;
}

If this function was to be compiled into assembler, it would leave, on the stack, three integers. There's basically nothing wrong with that, as the caller is responsible for cleaning up the stack, and, through the function prototype, it is known at compile time what return values a function leaves. (Think as an alternative that the function foo() would have returned a structure containing 3 integers, pretty legal in C++).

Now suppose that the assignment operator = is a function that can take an arbitrary number of parameters, not just one. Let's suppose the assignment operator is declared as:

inline operator =( ... )

That is, the assignment operator can take any number of parameters, from the stack. If there is written:

int a, b, c;
a, b, c = foo();

the assignment operator takes the 3 values from the stack that the function foo() has left, assigning them to a, b and c. The compiler would have to make sure, at compile time, that the number and the types of the arguments match. Of course, in the final implementation when the language is optimized, things doesn't take the detour via the physical stack, but this view is helpful in understanding whats going on.

With this in mind, the forwarding of multiple return values as function parameters is a no-brainer:

int bar( int x, int y, int z )
{
    return x + y + z;
}

int sum = bar( foo() );

The function bar() takes three parameters, which must be pushed onto the stack before calling the function. Instead of pushing single arguments, they could pretty much be left behind from the call to the function foo(). Again the compiler will check at compile time from the prototypes that everything matches together.

Thoughts, anyone?














May 30, 2002
"Christian Schüler" <cschueler@gmx.de> wrote in message news:ad3cnv$1dr6$1@digitaldaemon.com...

> Yes, that's the point. I was thinking about it how such a feature could be implemented. Let's suppose you can declare a function to have multiple returns in a compiled language (like C or D):
>
> int, int, int foo()
> {
>     return 1, 2, 3;
> }
>
> If this function was to be compiled into assembler, it would leave, on the stack, three integers. There's basically nothing wrong with that, as the caller is responsible for cleaning up the stack, and, through the function

D does not use the C calling convention (not always, at least). So it's wrong to assume that the caller will clean up the stack.

Also, how exactly do you want to pass something on the stack in such a way that callee can read it? Don't forget that you also have the PUSHed value of EBP, and then the return address. You can POP EBP first, of course, and then push the return values, but:

1) You then don't have access to local variables (since EBP
is restored to its old value), and registers might not be enough
(what if function returns 10 values?)

2) RET expects return address to be on top. This can be done by POPping it into the register, then pushing all the return values, then PUSHing the return address again. Kinda messy.

> prototype, it is known at compile time what return values a function
leaves.
> (Think as an alternative that the function foo() would have returned a
> structure containing 3 integers, pretty legal in C++).

But the structure isn't pushed on the stack! It is allocated somewhere, and pointer to it is passed in EAX.

By the way, in C, it was even worse: the structure was allocated once, and only one copy of it existed for one function... so each call rewrote data generated by previous one. C++ provides a workaround for this, at a cost of efficiency.




May 30, 2002
I think it's a cool idea,
I staying away from the ASM part.

int, int, int a()
{
...
    return x,y,z;
}

interprate the same as
int a(int out c, int out d)
{
...
   return x,y,z; //for all out params
}

interprate which would interparte to
int a(int out c, int out d)
{
...
   c = y;
   d = z;
   return x;
}

or alternativly

make it the same as doing,
{int a, int b , int c} a()
{
...
    return {x,y,z};
}

If speed is a concern users can just use the single version (but they'd probably end up using one of the later algorithms anyways) , because speed is not an issue in all parts of a problem.

PS - Does D allow for overloading of return types?



"Pavel Minayev" <evilone@omen.ru> wrote in message news:ad4djm$8na$1@digitaldaemon.com...
> "Christian Schüler" <cschueler@gmx.de> wrote in message news:ad3cnv$1dr6$1@digitaldaemon.com...
>
> > Yes, that's the point. I was thinking about it how such a feature could
be
> > implemented. Let's suppose you can declare a function to have multiple returns in a compiled language (like C or D):
> >
> > int, int, int foo()
> > {
> >     return 1, 2, 3;
> > }
> >
> > If this function was to be compiled into assembler, it would leave, on
the
> > stack, three integers. There's basically nothing wrong with that, as the caller is responsible for cleaning up the stack, and, through the
function
>
> D does not use the C calling convention (not always, at least). So it's wrong to assume that the caller will clean up the stack.
>
> Also, how exactly do you want to pass something on the stack in such a way that callee can read it? Don't forget that you also have the PUSHed value of EBP, and then the return address. You can POP EBP first, of course, and then push the return values, but:
>
> 1) You then don't have access to local variables (since EBP
> is restored to its old value), and registers might not be enough
> (what if function returns 10 values?)
>
> 2) RET expects return address to be on top. This can be done by POPping it into the register, then pushing all the return values, then PUSHing the return address again. Kinda messy.
>
> > prototype, it is known at compile time what return values a function
> leaves.
> > (Think as an alternative that the function foo() would have returned a
> > structure containing 3 integers, pretty legal in C++).
>
> But the structure isn't pushed on the stack! It is allocated somewhere, and pointer to it is passed in EAX.
>
> By the way, in C, it was even worse: the structure was allocated once, and only one copy of it existed for one function... so each call rewrote data generated by previous one. C++ provides a workaround for this, at a cost of efficiency.
>
>
>
>


May 30, 2002
> D does not use the C calling convention (not always, at least). So it's wrong to assume that the caller will clean up the stack.
>
It is quite easy for the compiler to force an appropriate calling convention on a multiple-return function.

> Also, how exactly do you want to pass something on the stack in such a way that callee can read it? Don't forget that you also have the PUSHed value of EBP, and then the return address. You can POP EBP first, of course, and then push the return values, but:
> 
> 1) You then don't have access to local variables (since EBP
> is restored to its old value), and registers might not be enough
> (what if function returns 10 values?)
> 
> 2) RET expects return address to be on top. This can be done by POPping it into the register, then pushing all the return values, then PUSHing the return address again. Kinda messy.
>
Compared to what else the compiler must do, this seems trivial.
Another approach would be to use an 'on-stack' calling convention.
(The D calling convention??)
Roughly (I think):
    int a, int b foo ( int c )  {  ...
Compiler creates 'pseudo-parameters' for returns--

    pop    EAX
    push  0
    push  0
    push  EAX

And then the normal entry code  --

    push  EBP
    mov   EBP, ESP

Now:
   a is at [ebp + 8]
   b is at [ebp + 12]
   c is at [ebp + 16]

Return is just a normal return:

    mov   ESP, EBP
    ret

and the caller does:

    call    foo
    pop   EAX                 ; get 'a'
    mov  [EBP - 4], eax  ; put it somewhere
    pop   EAX                 ; get 'b'
    add   ESP, 8             ; standard parameter trim

> By the way, in C, it was even worse: the structure was allocated once, and only one copy of it existed for one function... so each call rewrote data generated by previous one. C++ provides a workaround for this, at a cost of efficiency.

I suppose the 'C calling convention'  was invented prior to recursion or threading!! :-)

Karl Bochert




May 30, 2002
Hi,

"Pavel Minayev" <evilone@omen.ru> wrote in message news:ad4djm$8na$1@digitaldaemon.com...
> But the structure isn't pushed on the stack! It is allocated somewhere, and pointer to it is passed in EAX.
>
> By the way, in C, it was even worse: the structure was allocated once, and only one copy of it existed for one function... so each call rewrote data generated by previous one. C++ provides a workaround for this, at a cost of efficiency.

This is new to me. I just checked the code generated by Microsoft C/C++ in C mode. It allocates an anonymous stack variable for the return value in the calling code for each function call (not each invocation), which is perfectly reentrant. Small structs are returned in registers. I checked GCC too, and it also allocates the structure on the stack in the calling code. The behaviour you describe above sounds like a compiler bug to me.

Regards,
Martin M. Pedersen




May 30, 2002
"anderson" <anderson@firestar.com.au> wrote in message news:ad5clq$1jm8$1@digitaldaemon.com...

> PS - Does D allow for overloading of return types?

No, and I hope it never will.


May 30, 2002
"Karl Bochert" <kbochert@ix.netcom.com> wrote in message news:1103_1022775181@bose...

> I suppose the 'C calling convention'  was invented prior to recursion or threading!! :-)

Not really. I guess those guys were just too lazy to cover this case properly, bearing in mind that C was a tool made for private use at first...


« First   ‹ Prev
1 2 3 4