Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Unifying call and return

6 views
Skip to first unread message

Dan Sugalski

unread,
Nov 10, 2003, 9:50:09 AM11/10/03
to perl6-i...@perl.org
Okay, so it's time to get this taken care of.

Right now, the only true difference between a sub call and a return, at
least at the assembly level, is that we don't pass back a return
continuation when we're returning. You can, if you really want, even
arguably consider a sub PMC as a frozen semi-stateleless continuation.
(Not, mind, that I'd suggest it, as it'll make your brain hurt)

Anyway, call and return parameter lists are almost, but not quite, the
same. While that's not inherently bad, it seems pretty sub-optimal and
means two sets of parameter handling code, one for inbound call parms and
one for return parms. Seems silly, especially since they've got close to
identical semantics (since return params can be prototyped, as we can
return multiple values and may want to know what the assignment list for
the return is)

Anyway, time to unify. The question, then, is what gets kept and what gets
tossed. The return spec says we pass in the number of P, I, S, and N
params we're returning, which is fine, except we don't have enough
registers for that and the stuff we're passing in in I registers now. We
use all 5 when making a call. One overlaps with returns (The number of PMC
params) and that leaves us needing three ints.

We can toss some of the inbound stuff and make them optional properties on
the return continuation, which is where the return signature should live
as well (with the same property name as a sub or method's PMC signature)
which'd leave us needing two ints. I'm not sure if its worth trimming out
things from the registers to make space, or moving the two other counts to
N registers.

Oh, and we need to make sure the PMC count is in the same register for
calls as returns. It isn't now, which is a sloppy oversight and one we'll
fix as part of this.


Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk

Leopold Toetsch

unread,
Nov 10, 2003, 11:17:37 AM11/10/03
to Dan Sugalski, P6I
Dan Sugalski wrote:

> Anyway, time to unify. The question, then, is what gets kept and what gets
> tossed. The return spec says we pass in the number of P, I, S, and N
> params we're returning, which is fine,

I don't think we need these counts. If the call is prototyped, the
caller knows the type and count of registers. If the function might
return a varying amount of return values, depending, what the caller
specified in I3, the runtime checks and register setup on both sides
take by far more time, then what could be achived IMHO.
When the call is unprototyped the count of P-Regs is enough.


> We can toss some of the inbound stuff and make them optional properties on
> the return continuation,

I1 for call conventions is redundant, the overflow array P3 has the count.

> ... which is where the return signature should live


> as well (with the same property name as a sub or method's PMC signature)

Prototyped returns with a signature as property seems too heavy to me.

But first I'd like to have some clarification. E.g. "compile-time
prototype" vs unprototyped. Where and how do we have these prototypes?
What happens if a library sub is called either prototyped or not e.g.
from 2 different languages?

Currently we are already spending about 1/3 of the fib[1] Benchmark in
register setup for calling conventions. This doesn't take much time for
JIT, but all other run cores have a severe runtime penalty. We should
really try to reduce the needed register setup to a bare minimum.

[1] examples/benchmarks/fib.*


> Dan

leo

Dave Whipp

unread,
Nov 10, 2003, 12:14:07 PM11/10/03
to perl6-i...@perl.org
"Dan Sugalski" <d...@sidhe.org> wrote

> Right now, the only true difference between a sub call and a return, at
> least at the assembly level, is that we don't pass back a return
> continuation when we're returning

If one is coding a co-routine/iterator, then perhaps even this difference
might go away?

Dave.


Dan Sugalski

unread,
Nov 11, 2003, 12:15:56 PM11/11/03
to Dave Whipp, perl6-i...@perl.org

No, I can't see that happening unfortunately. It'd require the caller to
know whether it was calling a coroutine or normal sub.

OTOH, you *could* have the PMC for the coroutine be appropriately magic,
such that when you fetched it out of the symbol table you didn't get the
single shared sub PMC, but rather a custom PMC that had the setup
information in it that cached whatever was needed such that repeated
invocations had access to previously stored information...

Dan Sugalski

unread,
Nov 12, 2003, 11:12:51 AM11/12/03
to Leopold Toetsch, P6I
On Mon, 10 Nov 2003, Leopold Toetsch wrote:

> Dan Sugalski wrote:
>
> > Anyway, time to unify. The question, then, is what gets kept and what gets
> > tossed. The return spec says we pass in the number of P, I, S, and N
> > params we're returning, which is fine,
>
> I don't think we need these counts. If the call is prototyped, the
> caller knows the type and count of registers. If the function might
> return a varying amount of return values, depending, what the caller
> specified in I3, the runtime checks and register setup on both sides
> take by far more time, then what could be achived IMHO.
> When the call is unprototyped the count of P-Regs is enough.

I was thinking of the case where there are variable length returns for
N/I/S params. Which, arguably, ought to be there for calls too, to make
vararg stuff work properly. But since we're looking mainly to PMCs as our
primary datatype, I'm OK with tossing the I/N/S counts for returns.

> > We can toss some of the inbound stuff and make them optional properties on
> > the return continuation,
>
> I1 for call conventions is redundant, the overflow array P3 has the count.

Point. Gone.

> > ... which is where the return signature should live
> > as well (with the same property name as a sub or method's PMC signature)
>
> Prototyped returns with a signature as property seems too heavy to me.

Well... the prototype has to be somewhere so it can be queried at runtime.
The alternative is some sort of expected return prototype.


> But first I'd like to have some clarification. E.g. "compile-time
> prototype" vs unprototyped. Where and how do we have these prototypes?
> What happens if a library sub is called either prototyped or not e.g.
> from 2 different languages?

Prototypes are in there so the callee knows that the caller is using more
than just PMC registers. PMC counts are always there, since most of the
calls in real-world code will be just PMCs. It's not a prototype in the
general sense since it doesn't do much to indicate types beyond Parrot's
basic types -- it's certainly inadequate for HLL prototype management.

The reason to have them is compile-time uncertainty about call and return
types. Since we can't safely know with certainty what the definition of a
sub or method is, we need to pass into the sub/method a notation of what
parameters its getting. Otherwise we can run into the case where a sub is
expecting, say, three int and three string paramters but gets passed in
three num and one string parameter instead. While that's obviously an
error we'd like to have a chance for the callee to note that bad data has
been passed in and pitch an exception rather than, say, segfaulting
because the string register was NULL. (Which, arguably, is still
potentially a problem)

There's also the vararg issue -- I *really* hate having no metadata about
vararg parameters besides some crappy format string I have to parse or a
forced manual parameter count parameter. But there's a limit to how far
that annoyance will take me.

> Currently we are already spending about 1/3 of the fib[1] Benchmark in
> register setup for calling conventions. This doesn't take much time for
> JIT, but all other run cores have a severe runtime penalty. We should
> really try to reduce the needed register setup to a bare minimum.

I'll put together a proposed patch for PDD03 and post it to the list, and
we can hash it out. At the very least the PMC count will be in the same I
register for call and return...

Leopold Toetsch

unread,
Nov 12, 2003, 11:37:06 AM11/12/03
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:

> The reason to have them is compile-time uncertainty about call and return
> types. Since we can't safely know with certainty what the definition of a
> sub or method is, we need to pass into the sub/method a notation of what
> parameters its getting. Otherwise we can run into the case where a sub is
> expecting, say, three int and three string paramters but gets passed in
> three num and one string parameter instead.

I,N,S are only used, if the call is prototyped. When its prototyped,
registers are setup accordingly, that's it. Some run-time property with
the signature can't help here. Registers have some value or not. There is
no way to check, it something matches. All the checks are at compile
time IMHO.

When PMCs are passed, scalars get morphed to the appropriate type. When
e.g. a Sub is passed, and the subroutine awaits a scalar only, the
inexistent vtable pmc->get_<type> will catch this kind of error.

> There's also the vararg issue ...

and pass by value, r/o, by ref, copy and whatnot. Plus named arguments
and pie-thonic default arguments, if a param is missing ...

> I'll put together a proposed patch for PDD03 and post it to the list, and
> we can hash it out.

Good.

> Dan

leo

Dan Sugalski

unread,
Nov 12, 2003, 1:44:05 PM11/12/03
to Leopold Toetsch, perl6-i...@perl.org
On Wed, 12 Nov 2003, Leopold Toetsch wrote:

> Dan Sugalski <d...@sidhe.org> wrote:
>
> > The reason to have them is compile-time uncertainty about call and return
> > types. Since we can't safely know with certainty what the definition of a
> > sub or method is, we need to pass into the sub/method a notation of what
> > parameters its getting. Otherwise we can run into the case where a sub is
> > expecting, say, three int and three string paramters but gets passed in
> > three num and one string parameter instead.
>
> I,N,S are only used, if the call is prototyped. When its prototyped,
> registers are setup accordingly, that's it. Some run-time property with
> the signature can't help here. Registers have some value or not. There is
> no way to check, it something matches. All the checks are at compile
> time IMHO.

YHO would be incorrect here. There's a lot of runtime mutability, and
there's no guarantee that a sub or method has the same prototype at
runtime that it did at compiletime. (Though arguably methods are less of
an issue, as method signatures will almost never be known at compiletime)

> When PMCs are passed, scalars get morphed to the appropriate type. When
> e.g. a Sub is passed, and the subroutine awaits a scalar only, the
> inexistent vtable pmc->get_<type> will catch this kind of error.

Which is fine for all-PMC calls. Its the ones with mixed types that I'm
concerned with.

> > There's also the vararg issue ...
>
> and pass by value, r/o, by ref, copy and whatnot. Plus named arguments
> and pie-thonic default arguments, if a param is missing ...

All of which live at a somewhat higher level than this.

Leopold Toetsch

unread,
Nov 13, 2003, 2:36:44 AM11/13/03
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:

> YHO would be incorrect here. There's a lot of runtime mutability, and
> there's no guarantee that a sub or method has the same prototype at
> runtime that it did at compiletime.

*if* the pdds allow such weirdness with native types. Can we define
another property for such calls:

.sub _weird prototyped check_args

I still think, that the *common* case (i.e. fixed and known arguments on
both sides) should be as fast as possible.

> Dan

leo

0 new messages