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

Continuations don't close over register stacks

6 views
Skip to first unread message

Luke Palmer

unread,
Jan 6, 2004, 9:53:24 AM1/6/04
to Internals List
Aren't continuations supposed to close over the register stacks? In
this code:

.sub _main
I17 = 42
savetop
newsub P0, .Continuation, second
restoretop
invoke P0
second:
restoretop
print I17
print "\n"
end
.end

I should hope to get 42, but instead I get "no more I frames to pop."
They're not very good continuations if you have to treat them just like
an address stack!

Note that if they do, the code generated by the PCC constructs will have
to change to:

savetop
newsub P1, .RetContinuation, @retlabel
invoke
restoretop

Rather than saving right before the invoke. This makes a lot more sense
to me, anyway.

Luke

Melvin Smith

unread,
Jan 6, 2004, 10:20:30 AM1/6/04
to Luke Palmer, Internals List
At 07:53 AM 1/6/2004 -0700, Luke Palmer wrote:
>Aren't continuations supposed to close over the register stacks? In
>this code:
>
>I should hope to get 42, but instead I get "no more I frames to pop."
>They're not very good continuations if you have to treat them just like
>an address stack!

Currently the Copy-On-Write bit isn't being honored on the register pad stacks,
so restoretop (Parrot_pop_*()) is ignoring the fact that the stack it is
dealing with
is a readonly copy (taken by the continuation).

They are being marked, though, so its 1/2 complete.

I didn't finish the continuation implementation at the time, but I had assumed
someone had during my absence. (How is that for passing the blame? :) )

-Melvin


Leopold Toetsch

unread,
Jan 6, 2004, 11:30:30 AM1/6/04
to Melvin Smith, Luke Palmer, Internals List
Melvin Smith wrote:

> At 07:53 AM 1/6/2004 -0700, Luke Palmer wrote:
>
>> Aren't continuations supposed to close over the register stacks? In
>> this code:
>

> Currently the Copy-On-Write bit isn't being honored on the register pad
> stacks,

No. Register backing stacks are no more in the interpreter context and
neither stored nor restored by the continuation.

[ COWed stacks ]


> someone had during my absence. (How is that for passing the blame? :) )

Good. Pass it over to me :) COW copy of stacks and of other buffer-based
items is still broken. These need distinct headers so that they work
like COWed strings.


> -Melvin

leo


Luke Palmer

unread,
Jan 6, 2004, 6:41:20 PM1/6/04
to Leopold Toetsch, Melvin Smith, Internals List

Alright, I've got a pretty big incomplete patch here (see, when one has
a deadline on a compiler written with the assumption of working
continuations, one can be pretty determined :-).

It makes each chunk into a subclass of Buffer like so:

struct RegisterChunkBuf {
size_t used;
PObj* next;
};

And then, for example:

struct PRegChunkBuf {
struct RegisterChunkBuf buf;
struct PRegFrame PRegFrame[FRAMES_PER_CHUNK];
};

I want these things to be garbage collected, but DOD doesn't trace the
buffer. I can't seem to find a way to mark the frames without making
the chunks into PMCs (yuck). Is there a way to do this?

Thanks,
Luke

Jeff Clites

unread,
Jan 6, 2004, 8:24:19 PM1/6/04
to Luke Palmer, Internals List
On Jan 6, 2004, at 3:41 PM, Luke Palmer wrote:

> Leopold Toetsch writes:
>>
>> Good. Pass it over to me :) COW copy of stacks and of other
>> buffer-based
>> items is still broken. These need distinct headers so that they work
>> like COWed strings.
>
> Alright, I've got a pretty big incomplete patch here (see, when one has
> a deadline on a compiler written with the assumption of working
> continuations, one can be pretty determined :-).
>
> It makes each chunk into a subclass of Buffer like so:
>
> struct RegisterChunkBuf {
> size_t used;
> PObj* next;
> };

To do that properly, I think you need a pobj_t as the first struct
member, like string has:

struct parrot_string_t {
pobj_t obj;
UINTVAL bufused;
void *strstart;
UINTVAL strlen;
const ENCODING *encoding;
const CHARTYPE *type;
INTVAL language;
};

so maybe something like:

struct RegisterChunkBuf {
pobj_t obj;
UINTVAL bufused;
RegisterChunkBuf* next;
};

But also take a look at list.h and see if it's already doing what you
want to do; you may be able to do it directly.

> And then, for example:
>
> struct PRegChunkBuf {
> struct RegisterChunkBuf buf;
> struct PRegFrame PRegFrame[FRAMES_PER_CHUNK];
> };
>
> I want these things to be garbage collected, but DOD doesn't trace the
> buffer. I can't seem to find a way to mark the frames without making
> the chunks into PMCs (yuck). Is there a way to do this?

I think you'll need to add something to the root set tracing code
(probably trace_active_buffers() in src/dod.c). I'm not sure what all
of you stuff hangs off of, though.

Just some thoughts--I'm a little fuzzy on where these items are rooted.

JEff

Luke Palmer

unread,
Jan 6, 2004, 9:42:45 PM1/6/04
to Jeff Clites, Internals List
Jeff Clites writes:
> On Jan 6, 2004, at 3:41 PM, Luke Palmer wrote:
>
> >Leopold Toetsch writes:
> >>
> >>Good. Pass it over to me :) COW copy of stacks and of other
> >>buffer-based
> >>items is still broken. These need distinct headers so that they work
> >>like COWed strings.
> >
> >Alright, I've got a pretty big incomplete patch here (see, when one has
> >a deadline on a compiler written with the assumption of working
> >continuations, one can be pretty determined :-).
> >
> >It makes each chunk into a subclass of Buffer like so:
> >
> > struct RegisterChunkBuf {
> > size_t used;
> > PObj* next;
> > };
>
> To do that properly, I think you need a pobj_t as the first struct
> member, like string has:
>
> struct parrot_string_t {
> pobj_t obj;
> UINTVAL bufused;
> void *strstart;
> UINTVAL strlen;
> const ENCODING *encoding;
> const CHARTYPE *type;
> INTVAL language;
> };

Ah, no. That's how you create a new type of header. I need nothing
more than the simple buffer header. So I make a PObj and stick this
struct in its bufstart.

> so maybe something like:
>
> struct RegisterChunkBuf {
> pobj_t obj;
> UINTVAL bufused;
> RegisterChunkBuf* next;
> };
>
> But also take a look at list.h and see if it's already doing what you
> want to do; you may be able to do it directly.
>
> >And then, for example:
> >
> > struct PRegChunkBuf {
> > struct RegisterChunkBuf buf;
> > struct PRegFrame PRegFrame[FRAMES_PER_CHUNK];
> > };
> >
> >I want these things to be garbage collected, but DOD doesn't trace the
> >buffer. I can't seem to find a way to mark the frames without making
> >the chunks into PMCs (yuck). Is there a way to do this?
>
> I think you'll need to add something to the root set tracing code
> (probably trace_active_buffers() in src/dod.c). I'm not sure what all
> of you stuff hangs off of, though.

Did that. That works for the main part, but continuations and
who-knows-what-else are going to be holding references to parts of
these, so I'd like to mark these automatically.

Unless... hey! You just gave me an idea. I'll make a mark_regstack
that anything that holds on to one of these has to call as part of its
mark routine. I know, it seems like a no-brainer. Mustn't have had a
brain earlier :-)

> Just some thoughts--I'm a little fuzzy on where these items are rooted.

That's fine, and thanks. I learned most of these concepts earlier today
hacking on this patch...

Luke

Melvin Smith

unread,
Jan 6, 2004, 10:33:13 PM1/6/04
to Luke Palmer, Leopold Toetsch, Internals List
At 04:41 PM 1/6/2004 -0700, Luke Palmer wrote:
>I want these things to be garbage collected, but DOD doesn't trace the
>buffer. I can't seem to find a way to mark the frames without making
>the chunks into PMCs (yuck). Is there a way to do this?

I was about to answer your question when I saw your followup where
you answered it yourself.

Thats probably the way I would do it. A continuation can know how to
mark everything it closes over, and it doesn't have to be a PMC.
The brute force method is to copy the whole pad stack as soon
as a register frame is pushed or popped. As long as the stack is
tree based (where multiple copies of a set of frames can point
to the same parent), you only need to copy the current chunk, not
the whole stack, although most of the samples I ran at
the time never used more than a single register pad stack chunk (I think
it was 16 frames per chunk) so its probably an unnecessary short-cut,
just copy all chunks.

The downside to our implementation is in the return continuation case.
The common case is to create the continuation that you plan to
return with, and you already know there will be no need for
copy-on-write in most cases because typically the execution
path will return using that continuation, and that will then become
the "main" execution context. The original interpreter context and
all its associated stacks that existed at the time of the snapshot
will usually immediately be readonly orphans as soon as you activate the
return continuation (unless you saved the initial main context first).
It'd be more optimal to skip marking COW altogether in certain cases.

I think I've just confused myself so I'm sure I lost everyone.

The short of it is: the general case of closing over everything and
setting COW on everything for each return continuation is inefficient,
because the pattern becomes:

1) Freeze continuation B (mark all stacks COW in A, stacks that B references)
2) Activate return continuation B (replaces old interpreter
context/continuation A)
3) Continue execution which inevitably does stack modification and
causes a COPY + CLEAR COW bit on everything in B's now private copy.

B's private copy usually becomes the new "permanent" interpreter
context, until the next continuation (C, etc.) is activated. Taking return
continuations over
and over has the effect of repeatedly making the main context readonly.

Without reference counting (ugg) I'm not sure how else to implement
continuations correctly and achieve any sort of shortcut to skip copying
things. As I said to Dan when I first patched them in; hopefully
someone smarter than me would come along and do it better/faster, I only
care that it actually _works_, and for now, it doesn't, completely.

Also, good papers on VM design with continuations seemed to be rare 2 yrs ago
and I expect they still are.

Now that I've taken you on a 2 mile tangent, back to the topic.
I'd be happy to see your patch.

-Melvin


Jeff Clites

unread,
Jan 6, 2004, 10:57:30 PM1/6/04
to Luke Palmer, Internals List
On Jan 6, 2004, at 6:42 PM, Luke Palmer wrote:

> Jeff Clites writes:
>> On Jan 6, 2004, at 3:41 PM, Luke Palmer wrote:
>>
>>> It makes each chunk into a subclass of Buffer like so:
>>>
>>> struct RegisterChunkBuf {
>>> size_t used;
>>> PObj* next;
>>> };
>>
>> To do that properly, I think you need a pobj_t as the first struct
>> member, like string has:
>>
>> struct parrot_string_t {
>> pobj_t obj;
>> UINTVAL bufused;
>> void *strstart;
>> UINTVAL strlen;
>> const ENCODING *encoding;
>> const CHARTYPE *type;
>> INTVAL language;
>> };
>
> Ah, no. That's how you create a new type of header. I need nothing
> more than the simple buffer header. So I make a PObj and stick this
> struct in its bufstart.

Hmm, okay; I'm mildly confused then, but that's okay--I need to stare
at this a bit more. But if you put your struct inside the memory
pointed to by the bufstart of a simple buffer header, then I think that
compact_pool() is going to expect you to have a Buffer_Tail and such.
Since your struct is fixed-size, I would think it would make more sense
to make it a new type of header (like Stack_Chunk in stacks.h); but on
the other hand, I don't think we COW headers, and we need major COW
here. Hmm, still fuzzy.

>>> I want these things to be garbage collected, but DOD doesn't trace
>>> the
>>> buffer. I can't seem to find a way to mark the frames without making
>>> the chunks into PMCs (yuck). Is there a way to do this?
>>
>> I think you'll need to add something to the root set tracing code
>> (probably trace_active_buffers() in src/dod.c). I'm not sure what all
>> of you stuff hangs off of, though.
>
> Did that. That works for the main part, but continuations and
> who-knows-what-else are going to be holding references to parts of
> these, so I'd like to mark these automatically.

Yeah, I was on crack--what I said made no sense; of course these are
going to be hanging off of individual continuation PMCs, not directly
accessible from the interpreter struct, so these won't be part of the
root set.

> Unless... hey! You just gave me an idea. I'll make a mark_regstack
> that anything that holds on to one of these has to call as part of its
> mark routine.

Yep, at least from the vtable->mark() of the Continuation PMC. I see
there's already a mark_stack() being called there (its impl. is in
src/method_util.c). That's the right spot--I should have thought of
that before.

> I know, it seems like a no-brainer. Mustn't have had a brain earlier
> :-)

Apparently, I didn't either! It must be going around.

JEff

Leopold Toetsch

unread,
Jan 7, 2004, 4:46:01 AM1/7/04
to Luke Palmer, perl6-i...@perl.org
Luke Palmer <fibo...@babylonia.flatirons.org> wrote:
> It makes each chunk into a subclass of Buffer like so:

> struct RegisterChunkBuf {
> size_t used;
> PObj* next;
> };

That part is already answered: create a buffer_like structure.
*But* again register backing stacks are *not* in the interpreter context.

> Luke

leo

Jeff Clites

unread,
Jan 7, 2004, 11:52:27 AM1/7/04
to l...@toetsch.at, Luke Palmer, perl6-i...@perl.org

I don't understand what you are getting at. They are not physically
part of Parrot_Interp.ctx, but it holds pointers to them, right? So,
they need to be copied when the context is being duplicated. Is that
your point, or are you trying to say that they are not _logically_ part
of the context, or are not supposed to be?

JEff

Leopold Toetsch

unread,
Jan 7, 2004, 3:57:05 PM1/7/04
to Jeff Clites, perl6-i...@perl.org
Jeff Clites <jcl...@mac.com> wrote:
> On Jan 7, 2004, at 1:46 AM, Leopold Toetsch wrote:
>> That part is already answered: create a buffer_like structure.
>> *But* again register backing stacks are *not* in the interpreter
>> context.

> I don't understand what you are getting at. They are not physically
> part of Parrot_Interp.ctx, but it holds pointers to them, right?

No, they were in the context but aren't any more.

> ... So,


> they need to be copied when the context is being duplicated. Is that
> your point, or are you trying to say that they are not _logically_ part
> of the context, or are not supposed to be?

Exactly the latter:
That was AFAIK a design decision, when Dan did introduce CPS. At this
time register backing stacks went out of the continuation or whatelse
context - IIRC did Dan commit that to CVS himself.

So register stacks are *not* included in any context swapping, being it
a Continuation or some other context switch. That's it.

> JEff

leo

Luke Palmer

unread,
Jan 7, 2004, 8:37:50 PM1/7/04
to Leopold Toetsch, Jeff Clites, perl6-i...@perl.org
Leopold Toetsch writes:
> Jeff Clites <jcl...@mac.com> wrote:
> > On Jan 7, 2004, at 1:46 AM, Leopold Toetsch wrote:
> >> That part is already answered: create a buffer_like structure.
> >> *But* again register backing stacks are *not* in the interpreter
> >> context.
>
> > I don't understand what you are getting at. They are not physically
> > part of Parrot_Interp.ctx, but it holds pointers to them, right?
>
> No, they were in the context but aren't any more.
>
> > ... So,
> > they need to be copied when the context is being duplicated. Is that
> > your point, or are you trying to say that they are not _logically_ part
> > of the context, or are not supposed to be?
>
> Exactly the latter:
> That was AFAIK a design decision, when Dan did introduce CPS. At this
> time register backing stacks went out of the continuation or whatelse
> context - IIRC did Dan commit that to CVS himself.

In which case I feel obliged to contest that decision. The register
backing stacks are as much a part of the current state as the program
counter is.

I'm writing a compiler that makes heavy use of continuations for the
purpose of backtracking. If the register backing stacks aren't closed
over, and I am thus required to keep the state consistent myself, it is
impossible to use continuations for that purpose. Indeed, it becomes
impossible to use continuations for anything but simulating a control
stack, which is precisely what they are designed to get around.

Luke

Melvin Smith

unread,
Jan 7, 2004, 11:15:11 PM1/7/04
to Luke Palmer, Leopold Toetsch, Jeff Clites, perl6-i...@perl.org
At 06:37 PM 1/7/2004 -0700, Luke Palmer wrote:
>Leopold Toetsch writes:
> > Jeff Clites <jcl...@mac.com> wrote:
> > > On Jan 7, 2004, at 1:46 AM, Leopold Toetsch wrote:
> > >> That part is already answered: create a buffer_like structure.
> > >> *But* again register backing stacks are *not* in the interpreter
> > >> context.
> >
> > > I don't understand what you are getting at. They are not physically
> > > part of Parrot_Interp.ctx, but it holds pointers to them, right?
> >
> > No, they were in the context but aren't any more.
> >
> > > ... So,
> > > they need to be copied when the context is being duplicated. Is that
> > > your point, or are you trying to say that they are not _logically_ part
> > > of the context, or are not supposed to be?
> >
> > Exactly the latter:
> > That was AFAIK a design decision, when Dan did introduce CPS. At this
> > time register backing stacks went out of the continuation or whatelse
> > context - IIRC did Dan commit that to CVS himself.
>
>In which case I feel obliged to contest that decision. The register
>backing stacks are as much a part of the current state as the program
>counter is.

I tend to agree, but maybe Dan can explain. I looked back at the
CVS history and when I put continuations in, I did originally have
register stacks in the Parrot_Context (although they weren't yet
garbage collected). Dan since reverted that and put them back to
the top level interpreter object.

It seems to me they should be part of the context structure or
continuations become very messy and make save/restoring of
register pads almost useless in combination.

-Melvin


Luke Palmer

unread,
Jan 7, 2004, 11:43:34 PM1/7/04
to Melvin Smith, Leopold Toetsch, Internals List
Melvin Smith writes:
> The downside to our implementation is in the return continuation case.
> The common case is to create the continuation that you plan to
> return with, and you already know there will be no need for
> copy-on-write in most cases because typically the execution
> path will return using that continuation, and that will then become
> the "main" execution context. The original interpreter context and
> all its associated stacks that existed at the time of the snapshot
> will usually immediately be readonly orphans as soon as you activate the
> return continuation (unless you saved the initial main context first).
> It'd be more optimal to skip marking COW altogether in certain cases.

That's what the RetContinuation class does if I'm not mistaken. But
RetContinuation is a bit presumptuous about what is going to be done
above it. It must be guaranteed that nobody will try to use the
RetContinuation as a regular continuation without first promoting it
(I can see a way to make such promotion possible, described below).

There are three ways I see to reduce this overhead.

The first is to make the register stacks a linked list of single frames
-- no chunks. We could make object pools for each of the register frame
types to keep allocation quick. This would avoid copying altogether in
the common case, but it wouldn't get by marking everything COW. Plus,
the copying would still happen eventually (I can't think of a way to
safely unmark things COW without copying), just not as much. In
particular, the asymptotic complexity stays the same.

The second is to move the used counter out of the chunk and into the
stack structure. This way RetContinuations can be promoted into real
Continuations as long as the stack frame associated with the
RetContinuation has not yet exited.

The last way is to keep a set of 16 flags in each chunk that mark
whether a return continuation has been taken at that point, and if you
try to pop beyond that point, the stack is marked COW and copied. This
is effectively lazily promoting RetContinuations into real
continuations.

None of these seem optimal at this point, although #2 is definitely
something that could be useful.

Luke

Piers Cawley

unread,
Jan 8, 2004, 2:42:43 AM1/8/04
to Melvin Smith, Luke Palmer, Leopold Toetsch, Jeff Clites, perl6-i...@perl.org

I also agree. Continuations that don't save the register stacks are
about as much use as a chocolate teapot. Maybe it was supposed to be a
temporary reversion until GC got sorted.

Jeff Clites

unread,
Jan 8, 2004, 3:43:14 AM1/8/04
to Melvin Smith, Luke Palmer, Leopold Toetsch, perl6-i...@perl.org
On Jan 7, 2004, at 8:15 PM, Melvin Smith wrote:
>> Leopold Toetsch writes:
>> > Jeff Clites <jcl...@mac.com> wrote:
>> > > On Jan 7, 2004, at 1:46 AM, Leopold Toetsch wrote:
>> > Exactly the latter:
>> > That was AFAIK a design decision, when Dan did introduce CPS. At
>> this
>> > time register backing stacks went out of the continuation or
>> whatelse
>> > context - IIRC did Dan commit that to CVS himself.
>
> I tend to agree, but maybe Dan can explain. I looked back at the
> CVS history and when I put continuations in, I did originally have
> register stacks in the Parrot_Context (although they weren't yet
> garbage collected). Dan since reverted that and put them back to
> the top level interpreter object.

I think I'm just being dense, but looking at
include/parrot/interpreter.h it appears that they are in the context:

typedef struct Parrot_Context {
struct IRegChunk *int_reg_top; /* Current top chunk of int reg
stack */
struct NRegChunk *num_reg_top; /* Current top chunk of the float
reg
* stack */
struct SRegChunk *string_reg_top; /* Current top chunk of the string
* stack */
struct PRegChunk *pmc_reg_top; /* Current top chunk of the PMC
stack */

struct Stack_Chunk *pad_stack; /* Base of the lex pad stack */
struct Stack_Chunk *user_stack; /* Base of the scratch stack */
struct Stack_Chunk *control_stack; /* Base of the flow control
stack */
IntStack intstack; /* Base of the regex stack */
Buffer * warns; /* Keeps track of what warnings
* have been activated */
} parrot_context_t;


and

typedef struct Parrot_Interp {
struct IReg int_reg;
struct NReg num_reg;
struct SReg string_reg;
struct PReg pmc_reg;
struct Parrot_Context ctx; /* All the registers and stacks
that
matter when context
switching */
...

and in src/register.c we have:

void
Parrot_push_i(struct Parrot_Interp *interpreter, void *where)
{
/* Do we have any space in the current savestack? If so, memcpy
* down */
if (interpreter->ctx.int_reg_top->used < FRAMES_PER_CHUNK) {
memcpy(&interpreter->ctx.int_reg_top->
IRegFrame[interpreter->ctx.int_reg_top->used],
where, sizeof(struct IRegFrame));
interpreter->ctx.int_reg_top->used++;
}
...

The continuation code only saves interpreter->ctx, and not the stuff
that hangs off of it, but it seems pretty clear that the register save
stacks are attached to the interpreter context in the current sources.

So what am I missing?

JEff

Leopold Toetsch

unread,
Jan 8, 2004, 7:24:57 AM1/8/04
to Jeff Clites, perl6-i...@perl.org
Jeff Clites <jcl...@mac.com> wrote:

> I think I'm just being dense, but looking at
> include/parrot/interpreter.h it appears that they are in the context:

Sorry, yes. They are in the context but not saved. I mixed that up with
the registers themselves, which went out of the context.

leo

Jeff Clites

unread,
Jan 8, 2004, 12:30:25 PM1/8/04
to l...@toetsch.at, perl6-i...@perl.org

Okay--thanks for the confirmation.

So it looks like currently if a continuation is activated after the
stack frame in which it was created has exited, then
interpreter->ctx.int_reg_top may point to garbage (since the frame
_pointers_ are inside of the saved/restored context), so we'll end up
with indeterminate behavior. I don't think we have any guards against
that in place.

JEff

Dan Sugalski

unread,
Jan 12, 2004, 10:35:22 AM1/12/04
to Jeff Clites, l...@toetsch.at, perl6-i...@perl.org
Picking the last entry in this thread to reply to...

Here's the scoop with register stacks, stacks in general, and continuations.

The pointers to all these stack tops *should* be part of a
continuation, the same as the registers themselves should be. When a
continuation is taken, all the frames to the top should be marked
COW, and any access to the frames should copy them.

This does have the "interesting" side-effect of preserving integer
and float values across continuations, while allowing string and PMC
values to fluctuate. That is, since string and PMC stack entries are
pointers, changes to their contents propagate across continuations,
while changes to ints and floats do not. This has some ramifications
for code saving loop counters and whatnot on the stack. Short (though
not all that satisfying) answer--deal with it, or use a PMC.

Stack frames should probably just be globs of stack frame entry
memory with a pobj stuck on the front, allocated in one big hunk
(with pointers fixed up on allocation), to make DODing the things
easier. That is, a stack chunk should have the structure:

struct hunk {
struct pobj header;
INTVAL used;
INTVAL avail;
struct hunk *upchain;
struct regframe RegisterFrame[FRAMES_PER_HUNK];
}

more or less. When one's allocated the pointers in the header get set
to point to the body, or whatever, with flags set appropriately.
Modifying one marked COW should just copy the whole wad to a new hunk
and adjust the header pointers as need be.

To avoid massive thrashing, we should add a frameclose op, to mark a
frame as closed and start a new one regardless of how many entries
might still be free in the frame. When the interpreter is about to do
something that'll result in a COW marking of the frame it closes the
frame off and starts a new one (though marking a topmost entry as COW
could automatically close it. There are issues there)

Also to avoid thrashing some, adding a multi-entry pop will help.
Dropping 2 or more stack entries may toss an entire COW'd frame,
avoiding the need to make a copy just for the purpose of messing
around with the used/avail counts to drop it two or three ops later.
--
Dan

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

Leopold Toetsch

unread,
Jan 12, 2004, 11:47:03 AM1/12/04
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:

> struct hunk {
> struct pobj header;
> INTVAL used;
> INTVAL avail;

Only one of these is needed (and currently used: "used")

> struct hunk *upchain;
> struct regframe RegisterFrame[FRAMES_PER_HUNK];

I'd rather not have the store statically inside the hunk:
- small objects code currently has an upper limit for sized header pools
- more and differently sized pools impose negative effects on DOD times
- it needs more code duplication

leo

Dan Sugalski

unread,
Jan 12, 2004, 1:53:26 PM1/12/04
to l...@toetsch.at, perl6-i...@perl.org
At 5:47 PM +0100 1/12/04, Leopold Toetsch wrote:
>Dan Sugalski <d...@sidhe.org> wrote:
>
>> struct hunk {
>> struct pobj header;
>> INTVAL used;
>> INTVAL avail;
>
>Only one of these is needed (and currently used: "used")
>
>> struct hunk *upchain;
>> struct regframe RegisterFrame[FRAMES_PER_HUNK];
>
>I'd rather not have the store statically inside the hunk:
>- small objects code currently has an upper limit for sized header pools

Doesn't mean we have to use them, honestly. A separate arena for them is fine.

>- more and differently sized pools impose negative effects on DOD times

While true, we're already walking the stack frame areas, so I'm not
sure it'll work out that way.

>- it needs more code duplication

True, unless we yank out existing special-purpose code. If stack
frame PMCs end up looking to the DOD like array PMCs, well... less
code to deal with, since we'll be using the array code that already
exists. The tradeoff is code in the allocator, but I'm not sure it'll
actually be more code, just different code.

Leopold Toetsch

unread,
Jan 13, 2004, 5:07:58 AM1/13/04
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:

[ stack layout ]

>>I'd rather not have the store statically inside the hunk:
>>- small objects code currently has an upper limit for sized header pools

> Doesn't mean we have to use them, honestly. A separate arena for them
> is fine.

Each sized item (a Buffer_like header of one size class) has its own
arena. This isn't the problem. But the more different arenas we have,
the more we have to walk during DOD.

>>- more and differently sized pools impose negative effects on DOD times

> While true, we're already walking the stack frame areas, so I'm not
> sure it'll work out that way.

Yes. But only one arena. With the register frames in place, we would
have at least 2 more arenas with (1024+x) and (2048+x) bytes for x=12
currently (32-bit systems).

And the question is: should we unify other stacks (Pad, User, Control)
with the register frame stacks? stacks.c's implementation has
additionally a stack->next pointer which keeps a spare junk against
thrashing and it has a stack->limit to guard against wild running user
code.

leo

Dan Sugalski

unread,
Jan 15, 2004, 12:39:42 PM1/15/04
to l...@toetsch.at, perl6-i...@perl.org
At 11:07 AM +0100 1/13/04, Leopold Toetsch wrote:
>Dan Sugalski <d...@sidhe.org> wrote:
>
>[ stack layout ]
>
>>>I'd rather not have the store statically inside the hunk:
>>>- small objects code currently has an upper limit for sized header pools
>
>> Doesn't mean we have to use them, honestly. A separate arena for them
>> is fine.
>
>Each sized item (a Buffer_like header of one size class) has its own
>arena. This isn't the problem. But the more different arenas we have,
>the more we have to walk during DOD.

True enough, and it likely means that we end up with the size as a
variable rather than an embedded constant which slows things down a
bit, which isn't good.

> >>- more and differently sized pools impose negative effects on DOD times
>
>> While true, we're already walking the stack frame areas, so I'm not
>> sure it'll work out that way.
>
>Yes. But only one arena. With the register frames in place, we would
>have at least 2 more arenas with (1024+x) and (2048+x) bytes for x=12
>currently (32-bit systems).
>
>And the question is: should we unify other stacks (Pad, User, Control)
>with the register frame stacks? stacks.c's implementation has
>additionally a stack->next pointer which keeps a spare junk against
>thrashing and it has a stack->limit to guard against wild running user
>code.

Unifying the user and control stacks make sense. I don't see any
point in unifying the pad stack with anything, since it's not really
a stack as such, or at least ought not be, and it's got a more
complex linking structure anyway.

Leopold Toetsch

unread,
Jan 15, 2004, 4:01:44 PM1/15/04
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:

> Unifying the user and control stacks make sense. I don't see any
> point in unifying the pad stack with anything, since it's not really
> a stack as such, or at least ought not be, and it's got a more
> complex linking structure anyway.

Sorry for the unclear question: But currently Pad, User and Control
stacks use the stack layout of stacks.c. The questions was, to use a
common stack code for stacks.c and register.c.

The internals of pads are more complex, that's right, though the
implementation currently has a stack on the surface, i.e. C<new_pad>
pushes a new lexical pad on the pad stack. But searching lexicals is
O(n), I already suggested to use an OrderHash for that.

Do you have more on the layout of lexical pads?

leo

0 new messages