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

PDD20: An idea: Call frames as PMCs

5 views
Skip to first unread message

Chip Salzenberg

unread,
Nov 12, 2005, 10:45:21 PM11/12/05
to perl6-i...@perl.org
{ First, let's have a little applause for Leo for implementing the new
lexical scheme in, what, a week? He says it was only a half-day's
work, too. Nice work, dude! }

First some background for those of you who haven't read pdd20:

* Read pdd20.

But if you don't have time:

* LexPads are PMCs that provide a hash-like interface to lexical
variables.
* Each call frame may optionally have a LexPad.


Now, on to the idea:

HLLs need introspection on call frames and their lexicals. I figure
the easiest way to support HLL introspection of call frames is to make
call frames be PMCs. They would have METHODs like:

get_sub() # returns the subroutine being executed
get_lexicals() # returns the LexPad, or Null if none
get_caller() # returns the parent call frame

etc. We'd probably only need one new opcode, if that: something like

$P0 = callframe

to return the current call frame, which could then be walked with
get_caller(). I suppose we could toss it a version that takes an
INTVAL like:

$P0 = callframe 1

to walk up that many frames.

(Also, incidentally, Continuations would have METHOD get_frame().)


Now on to the questions, mostly for Leo:

(1) If this were implemented naively, would it be a significant
slowdown?

(2) How about hacks/cheats to make it fast?

(2a) For example, what if the internal call frame structure remains
its current non-PMC self, and the C<callframe> opcode actually creates
a _wrapper_ PMC? The only downside to that I could see is that PMC
identity would not callframe identity, and I could live with that if
it kept things fast.

(2b) Or maybe the PMC could be created on demand - only when the user
asks for it - and then cached in the low-level call frame structure,
so that would only need to be created once? This seems like the best
choice, assuming GC issues are manageable.

--
Chip Salzenberg <ch...@pobox.com>

Leopold Toetsch

unread,
Nov 13, 2005, 5:33:07 AM11/13/05
to Chip Salzenberg, perl6-i...@perl.org

On Nov 13, 2005, at 4:45, Chip Salzenberg wrote:

> { First, let's have a little applause for Leo for implementing the new
> lexical scheme in, what, a week? He says it was only a half-day's
> work, too. Nice work, dude! }

It was just half a day. The rest of the week was spent on unicode
improvements along with various fixes and cleanup in strings, charsets,
and encodings. The register allocator was already prepard in advance to
deal with the allocation of lexicals (another ~2 hours).

> etc. We'd probably only need one new opcode, if that: something like
>
> $P0 = callframe
>
> to return the current call frame, which could then be walked with
> get_caller(). I suppose we could toss it a version that takes an
> INTVAL like:
>
> $P0 = callframe 1
>
> to walk up that many frames.

We already have this kind of introspection: $ grep caller t/pmc/sub.t
(We can invent better syntax for it though [1])

> Now on to the questions, mostly for Leo:

[ callframe as PMC ]

[ ... ]

> ... This seems like the best


> choice, assuming GC issues are manageable.

This would be an enormous can of worms IMHO. The main problem is: no
one guarantees that a callframe PMC just stays in the subroutine, where
it is created. You could store the callframe PMC into globals, return
it from the sub, whatever. *If* the subroutine was left via a plain
C<returncc> the data of the callframe (Parrot_Context and register
space) are recycled immediately, but the callframe would still point to
it.
OTOH treating a callframe PMC as "normal" PMC would mean that its
contents is kept alive as long as the callframe PMC lives. This would
make timely destruction near to impossible in the case of an existing
callframe PMC.

The liveness of callframes is currently managed solely by
continuations, which makes this rather straightforward. A
RetContinuation can be used exactly once and the callframe is recycled
immediately. Creating a user accessible Continuation switches callframe
live-management to reference counts. And all the callframes up the
callchain have their RetCcontinuations converted to Continuations too
[2]. When the last continuation refering to a callframe dies, the
refcount reaches zero and the callframe gets recycled.

Complicating this already nasty stuff just for introspection is not a
good idea.

[1] I'd use just methods, either on the interpreter or a sub pmc:

interp."get_caller"(n_levels) # interpreter method or
sub_class = getclass 'Sub'
sub_class."get_caller"(n_levels) # class method 0 = this sub, 1 =
caller, ...
a_sub."get_caller"(nlevels) # obj method, if you already have
a sub

[2] A delimited continuation (shift/reset) would make this less
expensive

leo

Chip Salzenberg

unread,
Nov 14, 2005, 10:28:47 PM11/14/05
to Leopold Toetsch, perl6-i...@perl.org
On Sun, Nov 13, 2005 at 11:33:07AM +0100, Leopold Toetsch wrote:
> On Nov 13, 2005, at 4:45, Chip Salzenberg wrote:
> > $P0 = callframe 1

>
> We already have this kind of introspection: $ grep caller t/pmc/sub.t

OK, the Interpreter PMC interface is certainly flexible enough to
handle the introspections we need.

> OTOH treating a callframe PMC as "normal" PMC would mean that its
> contents is kept alive as long as the callframe PMC lives. This would
> make timely destruction near to impossible in the case of an existing
> callframe PMC.

OK, call frame as PMC looks like a non-starter. Consider it rescinded.

> [1] I'd use just methods, either on the interpreter or a sub pmc:
>
> interp."get_caller"(n_levels) # interpreter method or
> sub_class = getclass 'Sub'
> sub_class."get_caller"(n_levels) # class method 0 = this sub, 1 = caller, ...
> a_sub."get_caller"(nlevels) # obj method, if you already have a sub

Well, like I explained WRT lexicals, subroutines don't have callers
(nor do they have LexPads). _Call_frames_ have callers (and LexPads).
Which leads to:

The current introspection model only walks back the current call chain.
That's good but not enough. We also need to walk back any live call
chain, using any live continuation as a starting point.

How about if we implement get_caller() on Interpreter and Continuation,
rather than Interpreter and Sub? Alternatively, I guess we could make
get_caller() take two parameters: a Continuation as a starting point
(Null would mean "me"), and a number of levels up.

PS: I generally dislike the road-painting algorithm[*] implied by the
"nlevels" interface, but optimizing for introspection is silly, so
I'm not going to worry about it.

[*] Painting a line in the road but not moving your paint can:
1. paint a meter, walk one meter to the bucket
2. walk one meter, paint a meter, walk two meters to the bucket
3. walk two meters, paint a meter, walk three meters to the bucket
etc.
--
Chip Salzenberg <ch...@pobox.com>

Leopold Toetsch

unread,
Nov 15, 2005, 4:17:30 AM11/15/05
to Chip Salzenberg, perl6-i...@perl.org

On Nov 15, 2005, at 4:28, Chip Salzenberg wrote:

> On Sun, Nov 13, 2005 at 11:33:07AM +0100, Leopold Toetsch wrote:

> OK, call frame as PMC looks like a non-starter. Consider it rescinded.

Autrijus mentioned on #parrot that we'd need weak pointers at some
time. Then we can reconsider callframe PMCs.

>> interp."get_caller"(n_levels) # interpreter method or
>> sub_class = getclass 'Sub'
>> sub_class."get_caller"(n_levels) # class method 0 = this sub, 1 =
>> caller, ...
>> a_sub."get_caller"(nlevels) # obj method, if you already
>> have a sub
>
> Well, like I explained WRT lexicals, subroutines don't have callers
> (nor do they have LexPads). _Call_frames_ have callers (and LexPads).

Sure. The interpreter or the sub implement "get_caller" on behalf of
the callframe.

> Which leads to:
>
> The current introspection model only walks back the current call chain.
> That's good but not enough. We also need to walk back any live call
> chain, using any live continuation as a starting point.

Yup

> How about if we implement get_caller() on Interpreter and Continuation,
> rather than Interpreter and Sub?

... Interpreter and Sub and Continuation ;-) Dropping the interface
from Sub would again need to expose the RetContinuation, which converts
it (and all up the call chain) to real Continuations. If you already
have a Continuation then that's fine.

> Alternatively, I guess we could make
> get_caller() take two parameters: a Continuation as a starting point
> (Null would mean "me"), and a number of levels up.

Hmm. That are 3 params in the method: object, start, level. I guess
this needs first some thoughts, how we handle class methods, when
invoked by an instance of the class.

leo

Chip Salzenberg

unread,
Nov 15, 2005, 1:30:38 PM11/15/05
to Leopold Toetsch, perl6-i...@perl.org
On Tue, Nov 15, 2005 at 10:17:30AM +0100, Leopold Toetsch wrote:
> Autrijus mentioned on #parrot that we'd need weak pointers at some
> time. Then we can reconsider callframe PMCs.

Ah, weak pointers. I remember a time without weak pointers. It was a
happy time. Birds chirped in the trees....

> >How about if we implement get_caller() on Interpreter and Continuation,
> >rather than Interpreter and Sub?
>
> ... Interpreter and Sub and Continuation ;-) Dropping the interface

> from Sub would again need to expose the RetContinuation [...]

I certainly agree that exposing the RetContinuation, which
automatically promotes it and its whole call chain to a full
Continuation, is not something to do routinely.

However, it's a Bad Thing to put get_caller() or any other such call
frame introspection query into the Sub interface. You might as well
put a filename() query on a Unix inode.[*] That's gotta go, and it's
gotta not come back.

If caller introspection is common enough that invoking a method on
Interpreter is too slow or inconvenient -- and since we can't use
Continuations as the primary interface for caller queries -- I guess
the next step of optimization would be specialized opcodes for call
frame introspection. I hope we don't have to go there, and we can
stick with the Interpreter interface for common usage.

> > Alternatively, I guess we could make
> >get_caller() take two parameters: a Continuation as a starting point
> >(Null would mean "me"), and a number of levels up.
>
> Hmm. That are 3 params in the method: object, start, level. I guess
> this needs first some thoughts, how we handle class methods, when
> invoked by an instance of the class.

What's bad about how it works now? <- not a rhetorical question

[*] an inode may have as few as zero or as many as USHORT_MAX[**] names,
and finding them all requires scanning a disks's entire directory tree

[**] your mileage may vary
--
Chip Salzenberg <ch...@pobox.com>

Nicholas Clark

unread,
Nov 21, 2005, 6:47:52 AM11/21/05
to perl6-i...@perl.org
On Tue, Nov 15, 2005 at 10:30:38AM -0800, Chip Salzenberg wrote:

> [*] an inode may have as few as zero or as many as USHORT_MAX[**] names,
> and finding them all requires scanning a disks's entire directory tree

Although one should note that you can loose valid names off the top of your
directory tree with chroot, and from the leaves by mounting another filing
system over a non-empty directory.

(But of course if you're wondering why your filing system is fuller than it
should be given the size of all the files, it's more likely to be open but
unlinked files rather than the above 2 causes).

Ooops. Digression.

Nicholas Clark

0 new messages