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

Returning a new PMC from ops

0 views
Skip to first unread message

David Robins

unread,
Dec 18, 2002, 12:48:55 PM12/18/02
to perl6-i...@perl.org
What's the cleanest way to return a new PMC from an op?

e.g. Suppose I have a Vec2D_in_X_plane and a Vec2D_in_Y_plane and I add them
and want to always produce a Vec3D - is this correct (and is it efficient?):

void add(PMC* value, PMC* dest) {

if(value->vtable == &Parrot_base_vtables[enum_class_Vec2D_in_Y_plane]) {

PMC* ret = pmc_new(INTERP,enum_class_Vec3D);

/* set the correct x,y,z coords somehow (method? property?) */

if(dest->flags & PMC_active_destroy_FLAG)
dest->vtable->destroy(interpreter,dest);
*dest = *ret;

} else
/* handle other cases, crash and burn, whatever */
}

(Arising from this is: how do I check the type of an extension PMC? which I
presume still isn't supported so I'll accept "you can't, yet, because there
aren't any.")

(Yes, this is a bit of a stealth resurrection of part of the logical_not
thread, separated out into its own question; substitude "logical_not" for
"add", "RubyInteger" for "Vec3D_in_X_plane", and "RubyBoolean" for "Vec3D".)

Dave
Isa. 40:31

Leopold Toetsch

unread,
Dec 18, 2002, 3:11:52 PM12/18/02
to David Robins, perl6-i...@perl.org
David Robins wrote:

> What's the cleanest way to return a new PMC from an op?
>
> e.g. Suppose I have a Vec2D_in_X_plane and a Vec2D_in_Y_plane and I add them
> and want to always produce a Vec3D - is this correct (and is it efficient?):
>
> void add(PMC* value, PMC* dest) {
>
> if(value->vtable == &Parrot_base_vtables[enum_class_Vec2D_in_Y_plane]) {


always compare ids (vtables may change) - yes, many pmc's do like above,
but will need changes too.


> PMC* ret = pmc_new(INTERP,enum_class_Vec3D);


morph "dest" to be a "ret"?

unimplemented, unused, but your examples seems to be a typical test case.


> if(dest->flags & PMC_active_destroy_FLAG)
> dest->vtable->destroy(interpreter,dest);


Just forget "dest", it will be collected when unused.


> *dest = *ret;


Brr, will break for sure somewhere. PMCs might even be of different size
- Not now, but who knows)


> (Arising from this is: how do I check the type of an extension PMC?


All pretty much uncertain in distant fog for me.


> Dave


leo


David Robins

unread,
Dec 18, 2002, 4:50:47 PM12/18/02
to Leopold Toetsch, perl6-i...@perl.org
On Wed, 18 Dec 2002, Leopold Toetsch wrote:

> David Robins wrote:
> > if(value->vtable == &Parrot_base_vtables[enum_class_Vec2D_in_Y_plane]) {
>
> always compare ids (vtables may change) - yes, many pmc's do like above,
> but will need changes too.

What do you mean by ids? pmc->type()?

> > PMC* ret = pmc_new(INTERP,enum_class_Vec3D);
> morph "dest" to be a "ret"?
> unimplemented, unused, but your examples seems to be a typical test case.

"dest" could be any type, so it's not reasonable to expect any type to be
able to morph to my chosen destination PMC type (unless default->morph can
be changed to do a standard new for the type).

> > if(dest->flags & PMC_active_destroy_FLAG)
> > dest->vtable->destroy(interpreter,dest);
> Just forget "dest", it will be collected when unused.

Alright, I was thinking about GC but wasn't sure about destroy, but of
course the GC should do that too.

> > *dest = *ret;
> Brr, will break for sure somewhere. PMCs might even be of different size
> - Not now, but who knows)

Sure, I recognize why it's evil (binary copy and all), it's just my first
approximation/guess at how it should be done. (And with the present
semantics it should work, but should be wrapped in a function/macro.)

The thing is that dest _must_ be changed somehow, because it's the only
return value (the function itself is void). Actually, *dest needs to be
changed, because of course the dest pointer itself is a copy.

Maybe the ops should instead be changed to be

PMC* binaryOp(PMC* rhs) and
PMC* unaryOp(void)

after all, most of the time _someone_ has to create a destination PMC, and
except for the reuse angle (and 'new' is probably quite fast), the PMC
handling the op is in the best place to do it. Any particular objections to
that? (I take it the PMC Px regs are just pointers, so it shouldn't involve
any copying.)

> leo

Dave
Isa. 40:31

Leopold Toetsch

unread,
Dec 19, 2002, 2:17:33 AM12/19/02
to David Robins, perl6-i...@perl.org
David Robins wrote:

> On Wed, 18 Dec 2002, Leopold Toetsch wrote:


>>always compare ids (vtables may change) - yes, many pmc's do like above,
>>but will need changes too.

> What do you mean by ids? pmc->type()?


Yes, ->vtable->type().


>> morph "dest" to be a "ret"?
>>unimplemented, unused, but your examples seems to be a typical test case.
>>
>
> "dest" could be any type, so it's not reasonable to expect any type to be
> able to morph to my chosen destination PMC type (unless default->morph can
> be changed to do a standard new for the type).


In pdd02_vtables.pod morph() is defined to do exactly this.


> Maybe the ops should instead be changed to be
>
> PMC* binaryOp(PMC* rhs) and
> PMC* unaryOp(void)
>
> after all, most of the time _someone_ has to create a destination PMC, and
> except for the reuse angle (and 'new' is probably quite fast), the PMC
> handling the op is in the best place to do it.


The question is, how often may a PMC be reused, and how often has it to
be a new one. And for which vtable to you call above Op?.

The HL normally has smome rules, what the LHS has to be, so normally,
you do a "new Px, .Type" for the LHS and you have already a PMC of the
correct type. When the HL has different concepts, this might not be useful.


> Dave

leo


David Robins

unread,
Dec 19, 2002, 10:51:00 AM12/19/02
to Leopold Toetsch, perl6-i...@perl.org
On Thu, 19 Dec 2002, Leopold Toetsch wrote:

> David Robins wrote:
> > On Wed, 18 Dec 2002, Leopold Toetsch wrote:
> >> morph "dest" to be a "ret"?
> >>unimplemented, unused, but your examples seems to be a typical test case.
> > "dest" could be any type, so it's not reasonable to expect any type to be
> > able to morph to my chosen destination PMC type (unless default->morph can
> > be changed to do a standard new for the type).
> In pdd02_vtables.pod morph() is defined to do exactly this.

I was looking at the default.pmc code. I'll read the doc.

Doc says that

This method is primarily used when the interpreter has need of
coercing a PMC to a particular type, and isn't meant as a general
purpose casting tool. Compilers should only emit valid
transformations.

which doesn't seem to indicate that morph is the Right Thing, at least
conceptually (since I want to destroy the old PMC and create a new one all
the time, so I don't want the PMC wasting time trying to convert itself into
the new type if I'm just going to clobber the value anyway).

> > Maybe the ops should instead be changed to be
> >
> > PMC* binaryOp(PMC* rhs) and
> > PMC* unaryOp(void)
> >
> > after all, most of the time _someone_ has to create a destination PMC, and
> > except for the reuse angle (and 'new' is probably quite fast), the PMC
> > handling the op is in the best place to do it.
>
> The question is, how often may a PMC be reused, and how often has it to
> be a new one. And for which vtable to you call above Op?.

Well, if the HL (or even IMCC) is "$a = $b + $c", how should that be
translated? The existing type of $a sure shouldn't influence the result.
Supposing $b is a PerlInt, and $a maps to P0, $b to P1, and $c to P2, then:

add P0,P1,P2

calls PerlInt's add op, which in most cases wants the result to be a
PerlInt, except in some (when P2 is a float) it should be a PerlFloat, say:

PMC* add(PMC* value) {
if(value is not a float) {
PMC* ret = SELF.clone();
ret->cache.int_val += value->vtable->get_integer(INTERP);
return ret;
}
/* since addition commutes, flip it around so we get a float back */
return value->vtable->add(INTERP,SELF);
}

"value is not a float" is intentionally nebulous, there's no way to check if
a value is a float, except maybe by comparing the get_number/get_integer
returns, but that's slow and float roundoff will get you anyway. For
PerlInt it might suffice to check that 'value' is not a PerlFloat.

Not that C++ is a paragon of virtue, but operators in C++ work this way, as
does Perl5's overload extension.

Even in the existing case, the '$b' vtable will be called; that should not
be changed.

> The HL normally has smome rules, what the LHS has to be, so normally,
> you do a "new Px, .Type" for the LHS and you have already a PMC of the
> correct type. When the HL has different concepts, this might not be useful.

The (or one of the) subjects of the op has a better idea of the result type
than the caller (for example, $b and $c could be floats, or integers, and
the target should match, but the HL doesn't know which they are at compile
time).

Another interesting question: interoperability between languages. What do
you get when you add a PerlInteger and a RubyInteger? This seems like an
interesting can of worms, actually - the best idea I can come up with is
that a whole lot of morphing should be done at the interface ("interface"
defined as "the place where the two languages meet", in this case).

Leopold Toetsch

unread,
Dec 19, 2002, 1:47:48 PM12/19/02
to David Robins, perl6-i...@perl.org
David Robins wrote:

> On Thu, 19 Dec 2002, Leopold Toetsch wrote:


>>In pdd02_vtables.pod morph() is defined to do exactly this.
>

> This method is primarily used when the interpreter has need of
> coercing a PMC to a particular type, and isn't meant as a general
> purpose casting tool. Compilers should only emit valid
> transformations.
>
> which doesn't seem to indicate that morph is the Right Thing,


docs also say, "If the morphing
can't be reasonably done, for example if an integer is
asked to turn into a PerlArray, then the PMC is first
destroyed, then recreated as an empty PMC ..."

which seems to indicate that morph is the Right Thing ;-)

Currently we are "morphing" perlscalars all over the place. We do this
by replacing the vtable (and in case of strings) also changing flags.
This is what morph would do anyway, w/o actually destroying the PMC but
reusing the header (new vtable, new value, changed flags gives actually
the same as a freshly allocated PMC of the desired type).

The current way to change a float to an int or a string is probably just
what was intended to be done by the morph vtable func.


> PMC* ret = SELF.clone();


Cloning or making a new PMC for each LHS isn't that cheap. I think we
should reuse existing headers where possible. The same holds for string
headers, which are currently sparely reused.


> The (or one of the) subjects of the op has a better idea of the result type
> than the caller (for example, $b and $c could be floats, or integers, and
> the target should match, but the HL doesn't know which they are at compile
> time).


The HL probably doesn't know, which scalar type, but should know e.g.
aggregate yes/no or which aggregate.


> Another interesting question: interoperability between languages. What do
> you get when you add a PerlInteger and a RubyInteger? This seems like an
> interesting can of worms, actually - the best idea I can come up with is
> that a whole lot of morphing should be done at the interface ("interface"
> defined as "the place where the two languages meet", in this case).


Yep.

Actually we don't have a general scalar type now, because there is no
"general" scalar. Each HL probably wants different behaviours in
- type stability / type promotion
- range checking / auto extending
- range of values / promotion to bigint/float
- exception handling / warning / nothing

As perl6/P6C is the major user of current classes, there are perl* types
all over the place, e.g. a plain Array has a PerlUndef for non existent
values.

I'm not sure, if we even can have a general scalar (or Array) base type,
but when more HLs define their needs, we might find some common base,
which might be enough to make a general base class.


> Dave


leo

David Robins

unread,
Dec 19, 2002, 2:44:26 PM12/19/02
to Leopold Toetsch, perl6-i...@perl.org
On Thu, 19 Dec 2002, Leopold Toetsch wrote:

> David Robins wrote:
> > This method is primarily used when the interpreter has need of
> > coercing a PMC to a particular type, and isn't meant as a general
> > purpose casting tool. Compilers should only emit valid
> > transformations.
> > which doesn't seem to indicate that morph is the Right Thing,
>
> docs also say, "If the morphing
> can't be reasonably done, for example if an integer is
> asked to turn into a PerlArray, then the PMC is first
> destroyed, then recreated as an empty PMC ..."
>
> which seems to indicate that morph is the Right Thing ;-)

Well, what I was getting at was that if dest is, say, a RomanNumeralInteger
whose value is "mcmlxxxiv", I don't want it to waste time converting that to
the integer 1984 when I morph it to a PerlInt, if my next step is to
unilaterally set its value to 5.

> Currently we are "morphing" perlscalars all over the place. We do this
> by replacing the vtable (and in case of strings) also changing flags.

Which is sort of evil, but it makes up for that by being fast, I guess :D.

> This is what morph would do anyway, w/o actually destroying the PMC but
> reusing the header (new vtable, new value, changed flags gives actually
> the same as a freshly allocated PMC of the desired type).

And without calling init()... but in the cases that it is done, it isn't
needed. More knowledge of internals trading for efficiency.

> > PMC* ret = SELF.clone();
>
> Cloning or making a new PMC for each LHS isn't that cheap. I think we
> should reuse existing headers where possible. The same holds for string
> headers, which are currently sparely reused.

One would hope it could be made as cheap as Perl's SV allocation, which with
the plant/uproot mechanism, is probably pretty darn fast. Even allocating
memory isn't bad if it uses the fixed-size allocator.

Still, with the present "dest* PMC", how is a new PMC to be returned? Since
the address is the same, the old one won't be GC'd, thus destroy won't be
called (so it will need to be called explicitly as I had in an earlier
message). Then the new vtable/flags have to be set and init called (which
is often a no-op and can be skipped, especially if an assignment is to be
done right away).

Maybe a "placement pmc_new" would be a good idea (assuming that the PMC
header remains the same size for all types), i.e.

PMC*
pmc_new_placement(struct Parrot_Interp* interpreter, PMC* dest,
INTVAL base_type)
{


if(dest->flags & PMC_active_destroy_FLAG) {
dest->vtable->destroy(interpreter,dest);
}

dest->vtable = Parrot_base_vtables+base_type;

if(!dest->vtable || !dest->vtable->init) {
PANIC("NULL vtable used");
return NULL;
}

dest->vtable->init(interpreter, dest);

return dest;
}

with appropriate variants (init/noinit).

> > The (or one of the) subjects of the op has a better idea of the result type
> > than the caller (for example, $b and $c could be floats, or integers, and
> > the target should match, but the HL doesn't know which they are at compile
> > time).
> The HL probably doesn't know, which scalar type, but should know e.g.
> aggregate yes/no or which aggregate.

I take it you mean for "no aggregate" it should set the dest type to be a
"generic scalar", but what about languages that don't have such a "base
class" (or that don't know at runtime even if they have an aggregate or
not, such as more "pure OO" languages)?

Leopold Toetsch

unread,
Dec 20, 2002, 3:41:31 AM12/20/02
to David Robins, perl6-i...@perl.org, Dan Sugalski
David Robins wrote:

> On Thu, 19 Dec 2002, Leopold Toetsch wrote:


> Well, what I was getting at was that if dest is, say, a RomanNumeralInteger
> whose value is "mcmlxxxiv", I don't want it to waste time converting that to
> the integer 1984 when I morph it to a PerlInt, if my next step is to
> unilaterally set its value to 5.


No need to look at *dest, just tell it to convert itself to a PerlInt
with value 5.

>>Currently we are "morphing" perlscalars all over the place. We do this
>>by replacing the vtable (and in case of strings) also changing flags.

> Which is sort of evil, but it makes up for that by being fast, I guess :D.


Yes, and might need changes in the future.

This discussion might yield more specific results, if Dan /* hallo -
cc'ed */ could tell us, what the usage of morph really is.


>>Cloning or making a new PMC for each LHS isn't that cheap. I think we
>>should reuse existing headers where possible. The same holds for string
>>headers, which are currently sparely reused.


> One would hope it could be made as cheap as Perl's SV allocation, which with
> the plant/uproot mechanism, is probably pretty darn fast. Even allocating
> memory isn't bad if it uses the fixed-size allocator.


Normally PMC's are returned from the free list, but when each assignment
does make a new header, you would spend a lot of time in DOD/GC.
(reusing a string header for substr gave IIRC +50% speed improvement in
life.pasm)


> Still, with the present "dest* PMC", how is a new PMC to be returned? Since
> the address is the same, the old one won't be GC'd, thus destroy won't be
> called (so it will need to be called explicitly as I had in an earlier
> message). Then the new vtable/flags have to be set and init called


Yep, that is - thinks /me - what should be hidden inside morph.


> Dave

leo

0 new messages