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

set vs. assign, continued: 'add' vs. 'add!'

6 views
Skip to first unread message

Togos

unread,
Aug 13, 2003, 11:30:56 PM8/13/03
to perl6-i...@perl.org
add(out INT, in INT, in INT)
add(out NUM, in NUM, in INT)
add(out NUM, in NUM, in NUM)
add(in PMC, in PMC, in INT)
add(in PMC, in PMC, in NUM)
add(in PMC, in PMC, in PMC)

This kind of ties into the 'set vs assign' issue.
Add behaves differently depending on whether
the first parameter is an integer/float or a PMC.
If it's a PMC, then it changes the value of the PMC,
but if it's a integer or float, it just puts a
new value in the register.

When I say in IMCC:

$P0 = $P1 + $P2

, I expect it to create a new value and store it in
$P0, not give me a segfault because I didn't say

$P0 = new <figure out what the heck type
$P0 is supposed to be based
on the types of $P1 and $P2>

first. Now

$P0 <- $P1 + $P2

(or whatever)
, that'd be a different story :-) Maybe ops that
operate by changing the values of existing PMCs
should be postfixed somehow, kind of like
Ruby's (and Lisp's?) conventions of postifixing
functions that modify values with "!". So then
there would be 2 add (and sub, and mult, and incr,
etc, etc.) ops:

add(out PMC, in PMC, in PMC)

which puts a new value in $1 just like the
integer and float variants, and

add!(in PMC, in PMC, in PMC)

which alters the PMC in $1.

Using the first version would allow PMCs
to decide for themselves what kind of
thing you should get as a result, which is
probably what you actually want to happen
most of the time.


__________________________________
Do you Yahoo!?
Yahoo! SiteBuilder - Free, easy-to-use web site design software
http://sitebuilder.yahoo.com

Brent Dax

unread,
Aug 14, 2003, 1:07:31 AM8/14/03
to TOGoS, perl6-i...@perl.org
TOGoS:
# When I say in IMCC:
#
# $P0 = $P1 + $P2
#
# , I expect it to create a new value and store it in
# $P0, not give me a segfault because I didn't say
#
# $P0 = new <figure out what the heck type
# $P0 is supposed to be based
# on the types of $P1 and $P2>
#
# first.

Then your expectations are wrong.

I think you may be losing sight of the fact that most users will *never*
interact with PASM or PIL directly. Most users will have a
parser/compiler/optimizer between them and PIL--one that was written,
tested and debugged by experts. The few people who work with raw
assembly or IL should know what they're doing. While I have no problem
with IMCC-time warnings about constructs like this ("Use of
uninitialized register $P0 in add"), I don't think we should coddle the
user at this level.

Moreover, there really isn't any way to do what you want to do here.
There is no way for the add opcode to intuit what type should be put
into $P0. That's ultimately the host language's decision, not Parrot's,
and the host language may have complicated rules to decide it. And even
if we did do something like this, we'd be penalizing calls to add that
*do* have correct arguments.

--Brent Dax <br...@brentdax.com>
Perl and Parrot hacker

"Yeah, and my underwear is flame-retardant--that doesn't mean I'm gonna
set myself on fire to prove it."

Togos

unread,
Aug 14, 2003, 1:54:40 AM8/14/03
to Brent Dax, perl6-i...@perl.org

Yes, I realised when I wrote this that it
may be impractical to have add(pmc) ops with
the semantics I suggested (you'd
probably have to add new vtable entries,
for one thing), but the op with the existing
semantics should at least have a different
syntax. As it is, the add(ints) op and the
add(pmcs) ops do very different things (one
has 'set' while the other has 'assign' semantics),
but they look exactly the same. If not at
the PASM level, then they should at least
be different at the IMCC level (":=" and "=", or
"=" and "<-" or whatever for set and assign).

Just because most people that write the stuff are
supposed to know what they're doing doesn't mean
that the interface shouldn't be kept as clean and
consistent (I can spell "consistent", now!)
as possible for those of us (and our
compilers) who *do* work with it, especially
when it doesn't actually cost anything, except
maybe initial development time (Remember: it's
supposed to be Good, not necessarily Quick or
Cheap. Otherwise Parrot'd be done by now :-)

I sense the 'only people who really know
what they're doing wil use this interface,
anyway, so we don't have to make it nice'
attitude in a few other places... most
notably the Windows API. Ever tried to do
a fork() with the Windows API? Compared
to the simplicity of Unix's fork(), the
Windows version just makes me feel dirty.
That's why I try to avoid programming for
Windows. Hint, hint ;-)

But aside from all that, I still think
that the set semantics for add, etc. would
make a lot of sense in many cases, because
the PMC that is being addded will know
how best to return. Adding something to a PythonInt
would return a PythonInt, while adding to a PerlInt
would return a PerlInt. Or a PMC could return
something completely different when you add
something to it. So it wouldn't be the add
opcode deciding what to do; it would be the
PMC. Using the assign semantics the PMC that
you are adding to has no control over what you
end up with (does it?). Sometimes this may be
what you want, But in my view, add with set
semantics will be more common in dynamicly-typed
languages, so it ought to be at least as well
supported. But if not, at least try not to
confuse set and assign, so much. Having to say
what you mean could save compiler writers and
debuggers from a lot of headaches. :-P

Brent Dax

unread,
Aug 14, 2003, 2:25:07 AM8/14/03
to TOGoS, perl6-i...@perl.org
TOGoS:
# syntax. As it is, the add(ints) op and the
# add(pmcs) ops do very different things (one
# has 'set' while the other has 'assign' semantics),
# but they look exactly the same. If not at

I think that's mostly because there's no difference between set
semantics and assign semantics for ints. You can think of them as both
having assign semantics, if you like.

# I sense the 'only people who really know
# what they're doing wil use this interface,
# anyway, so we don't have to make it nice'
# attitude in a few other places... most
# notably the Windows API.

You don't have to lecture me on the Windows API--I use Windows, and I
have the scars to prove it. ;^) But in this case, we're doing it
because:

a. It speeds things up.
b. It avoids a very difficult DWIM.
c. It simplifies the syntax.

Sure, you don't need to use 'new' on an I register, while you do on a P
register--but you *never* need to use 'new' on an I, if memory serves.
So that's a consistent...uh...inconsistency between I and P registers.

# But aside from all that, I still think
# that the set semantics for add, etc. would
# make a lot of sense in many cases, because
# the PMC that is being addded will know
# how best to return.

The thing is, the target PMC needs a way to react to the fact that it's
being assigned to. The real semantics we want are something like:

assign $P0, (add $P1, $P2)

(Assuming that ops behaved that way...)

I don't think any vtable ops actually allocate new PMCs and return
them--to my knowledge, they always operate on a target PMC. I believe
that this is done for speed reasons, although I'm not sure about
that--after all, it's much cheaper to allocate target PMCs only when you
need them than to allocate them when you don't.

K Stol

unread,
Aug 14, 2003, 12:52:07 PM8/14/03
to Brent Dax, TOGoS, perl6-i...@perl.org

If I remember correctly, the "clone" op allocates a new PMC and stores it in
the target reg.
$P1 = clone $P2 # $P1 may be NULL, at least contents of $P1 are lost
after this op.

Leopold Toetsch

unread,
Aug 14, 2003, 1:46:03 AM8/14/03
to Togos, perl6-i...@perl.org
Togos <chumps...@yahoo.com> wrote:

> add(out PMC, in PMC, in PMC)

> add!(in PMC, in PMC, in PMC)

While that is all fine and useful, we would need that for e.g. keyed set
and get too (store/get a reference or value). So that would lead to
effectively almost doubling our opcode count.
This can lead to less code locality and instruction cache misses, which
I don't like.

leo

Togos

unread,
Aug 14, 2003, 4:36:16 AM8/14/03
to Brent Dax, perl6-i...@perl.org
> that's mostly because there's no difference
> between set
> semantics and assign semantics for ints. You can
> think of them as both
> having assign semantics, if you like.

Works better to think of only 'set' semantics.
I think of it like this, and it makes things
work out very nicely:

Think of registers as little boxes. Now think of
the contents of registers as little tickets you
can store in the boxes. The tickets aren't the
objects themselves, they are merely references
to the objects, which stored in the back room.
The only difference between PMCs and integers is
that you don't actually need to store any integers
in the back room, as the references on the tickets
contain all the information you could ever want
about the integer, anyway (all their bits).

Now, you can swap tickets into boxes
(we call this "set", or "="), but we can
also go into the back room and alter the
values referenced by tickets (we call
this "assign", and TOGoS suggests that
we have a "<-" operator for it)

So you can't _assign_ a value to an
integer, as there is not actually
anything in the back room:
I0 = 5
assign I0, 6
would mean to put 5's ticket in I0, and then
change 5. But you can't change 5. 5 is just
5 and that's all there is to it. If you changed
5's value to 6, then it wouldn't be 5 anymore!

Anyone who tries to do
5 = 6
deserves whatever errors come to them ;-)

However, you can _set_ the value in an integer
register to an integer
I0 = 5
I0 = 6
with no problem. Here you are simply putting a
different ticket in the box.

Similarly
P0 = 3
Should not alter the PMC stored in the back
room referenced by the ticket in P0, but
just replace the ticket with a different one. Or
else raise an error saying that IMCC doesn't
know how to do put an integer ticket into a PMC
box. But what it shouldn't do is go into the
back room and screw with your PMCs, as that
would be inconsistent with the syntax/symantics
for assigning values to non-PMCs.

Currently,
I0 = I1 + I2
only changes which int is in the register I0,
while
P0 = P1 + P2
leaves the ticket alone and alters the
PMC referenced by P0. Now everywhere
you referenced that PMC, you'll be getting the
new value, whereas with the ints you still have
the old value. It may be the case that that's what
you wanted, but you shouldn't pretend that
going into the back room and changing values
is the same thing as swapping a ticket into a box.

> But in this case, we're doing it
> because:

> a. It speeds things up.
> b. It avoids a very difficult DWIM.

I understand why you might not want
to have 'set' _semantics_ for add(pmc),
(higher opcode count, as Leo pointed out)
but I was just talking about _syntax_, here.
It's fine if the PMC add op only has
assign semantics for practical reasons,
as long as it doesn't pretend to have
set semantics by using the same syntax
as the int add op which does have set
semantics.

> c. It simplifies the syntax.

It makes one operator mean 2 completely
different things depending on context.
I guess it depends what kind of simplicity
you want. You seem to prefer fewer operators,
while I would prefer each operator to only mean
one thing. Having separate operators for set
and assign would arguably make IMCC code (and
compilers that target IMCC) more maintainable.

I wouldn't worry so much about the PASM syntax:
add(p1,p2,p3) # OK, whatever
add(i1,i2,i3)
As that is low level enough that you'll
have to read the docs to know exactly
how to use it, anyway.

But IMCC seems high-level enough (it's got infix
operators, for one thing) that it should be
disambiguated. IMCC syntax seems to imply a certain
meaning more than PASM opcode names do, so it's
more important that the implied meaning is the true
meaning.

P1 = new PerlInt
P1 <- P2 + P3 # When you see "<-" instead of "=",
# You know that it's operating on
# an existing value, not just
# altering the contents of
# registers, as your experience
P0 = P1 # would suggest happens when
I0 = I1 + I2 # you use the "=" operator.

> Sure, you don't need to use 'new' on an I register,
> while you do on a P
> register--but you *never* need to use 'new' on an I,
> if memory serves.
> So that's a consistent...uh...inconsistency between
> I and P registers.

Ints never use 'new' or 'assign'. But when
ints and PMCs share a common syntax, as they
do with
a = b + c
, it should mean basically the same thing for both.

> # But aside from all that, I still think
> # that the set semantics for add, etc. would
> # make a lot of sense in many cases, because
> # the PMC that is being addded will know
> # how best to return.
>
> The thing is, the target PMC needs a way to react to
> the fact that it's
> being assigned to. The real semantics we want are
> something like:
>
> assign $P0, (add $P1, $P2)
>
> (Assuming that ops behaved that way...)

Yes, and I was suggesting that maybe that's
not what we always wanted to do. When we do
want to do that, though, it should be obvious
from the syntax that we're doing an assign,
and not a set.

Summary of my argument:
the "=" operaror should always put a new value in
the specified register, whether it be a PMC, and
int, or whatever. If you're not putting a new value
in the register, but modifying the value already in
there, then use the "<-" operator! :-)

(Now, I don't care if you replace "=" and "<-"
with different strings, as long as they're
different from each other.)

*creak* OK I'll shut up, now.
About this, anyway.
For now.
:-)

Leopold Toetsch

unread,
Aug 14, 2003, 4:40:21 AM8/14/03
to Togos, perl6-i...@perl.org
Togos <chumps...@yahoo.com> wrote:

[ add semantics ]

> ... Adding something to a PythonInt


> would return a PythonInt, while adding to a PerlInt
> would return a PerlInt.

That's anway the way these oopcode work:

add PDest, PSsrc, PVal

PSrc->vtable->add ...
do add according to type, then:

PDest->vtable->set_integer_native(...result)

so its up to the vtable set_integer_native to do the right thing,
which is e.g. morph()ing the destination to the desired type.

leo

Gordon Henriksen

unread,
Aug 14, 2003, 9:52:07 AM8/14/03
to TOGoS, perl6-i...@perl.org
On Thursday, August 14, 2003, at 04:36 , TOGoS wrote:

> P1 = new PerlInt
> P1 <- P2 + P3 # When you see "<-" instead of "=",
> # You know that it's operating on
> # an existing value, not just
> # altering the contents of
> # registers, as your experience
> P0 = P1 # would suggest happens when
> I0 = I1 + I2 # you use the "=" operator.

When I see <-, I think of processor user manuals, and there generally <-
means "poke into a register"—exactly the opposite of what you want it to
mean. But, in actuality, it'll also be used like memory[GPR1+GPR2] <-
GPR3 or GPR3 <- memory[GPR1+GPR2], just less often, since loads and
stores are much less common than register-to-register operations in most
ISAs. But, in fact, <- means exactly the same thing that = does. Much
like set and assign are synonyms.

If there's any need whatsoever to disambiguate the syntax, why make up
new syntax? Why not use a syntax which is already familiar to every
low-level programmer? How about this for the store (as in load/store)
semantics:

*P1 = I1 + I2

—

Gordon Henriksen
mali...@mac.com

Benjamin Goldberg

unread,
Aug 15, 2003, 12:27:41 PM8/15/03
to perl6-i...@perl.org

Noone is saying that the add opcode *should* try to intuit that -- he's
trying to say that there should be two versions of the add opcode, one
of which uses assign semantics, and the other of which uses set
semantics.

> That's ultimately the host language's decision, not Parrot's,
> and the host language may have complicated rules to decide it.

It would be the host language's decision about which opcode to use.

I suppose that if one writes:
$P0 = $P1 + $P2

, then it might be parrot's (imcc's) decision about which opcode to
compile it to... but of course, the host language can avoid that
ambiguity by writing it out explicitly, as "add_set( $P0, $P1, $P2 )" or
"add_assign( $P0, $P1, $P2 )". Or perhaps, require some pragma
indicating to imcc that all $Px = $Py + $Pz should use the add or assign
versions of the ops.

> And even if we did do something like this, we'd be penalizing calls to
> add that *do* have correct arguments.

Define "correct".

If add only has assign semantics, then any language which wants set
semantics must first allocate a new pmc, then add with that as the
target.

If add were to only have set semantics, then any language which wants
assign semantics must first add, then assign (and then, I suppose, null
out the pmc which add created for it's target so it can be GC'd.)

(Note that if add were to have set semantics, it wouldn't be parrot's
decision about what type of pmc the target should have; that decision
would belong to the add vtable entry of the first of the two things
being added together.)

Clearly, having *only* an add with set semantics would be wrong, since
there'd be no way to avoid having the extra pmc created. (Not to
mention, even if we want to assign the result into one of the pmcs being
added together, our result still has to go into a register other than
one of them; this might require spilling registers first.)

However, having *only* an add with assign semantics isn't perfect
either, since to simulate set semantics, we have to allocate a target
pmc beforehand, which means must decide what type the new pmc should be
-- either we allocate a PerlUndef (or some other fixed type, which
regardless of what it is, might just possibly be the *wrong* type).
This puts the burden of the decision about what type the target should
be allocated as completely on the host language's shoulders, and doesn't
let the pmc's involved decide. Well, I suppose one could do:
result_type_for_add $I0, $P1, $P2
$P0 = new $I0


$P0 = $P1 + $P2

, but this is rather clumsy. I'd rather have two versions of add, one
with set/create semantics and the other with assign/mutate semantics.

--
$a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca
);{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "$@[$a%6
]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}

Togos

unread,
Aug 15, 2003, 4:07:05 PM8/15/03
to perl6-i...@perl.org
> Brent Dax wrote:
> > TOGoS:
> > # When I say in IMCC:
> > #
> > # $P0 = $P1 + $P2
> > #
> > # , I expect it to create a new value and
> > # store it in

> > # $P0, not give me a segfault because I didn't say
> > #
> > # $P0 = new <figure out what the heck type
> > # $P0 is supposed to be based
> > # on the types of $P1 and $P2>
> > #
> > # first.
> >
> > Then your expectations are wrong.
>
> Noone is saying that the add opcode *should* try to
> intuit that -- he's
> trying to say that there should be two versions of
> the add opcode, one
> of which uses assign semantics, and the other of
> which uses set
> semantics.

Yap. *That's* what I was saying :-)

The one problem with this is that more vtable entries
would need to be added, and as Leo pointed out:

> While that is all fine and useful,
> we would need that for e.g. keyed set
> and get too (store/get a reference or value).
> So that would lead to effectively almost
> doubling our opcode count. This can lead to
> less code locality and instruction cache
> misses, which I don't like.

There may be something I'm missing, but I don't
understand why we need a keyed version of just
about every opcode. If it wasn't for that,
this wouldn't be so much of a problem. I find
it a bit strange that there is a keyed version
of 'add', anyway. If you're doing a lot of
operations on integers stored in arrays it could
help performance, but how often does this actually
happen? Maybe there should be a add_keyed_int
opcode/vtable entry, but not a generic add_keyed
entry? :-S

Dan Sugalski

unread,
Aug 15, 2003, 3:40:02 PM8/15/03
to perl6-i...@perl.org
On Fri, 15 Aug 2003, TOGoS wrote:

> There may be something I'm missing, but I don't
> understand why we need a keyed version of just
> about every opcode.

It avoids having to create a lot of temporary PMCs with weird magic on
them, avoids having to create temporary PMCs in general, and allows the
container to hold the value overloading code.

At some point I'll put together a more comprehensive formal reasoning for
it, but for now accept that you have to do it this way and cope.

Dan

Gordon Henriksen

unread,
Aug 15, 2003, 5:36:42 PM8/15/03
to Perl6 Internals (parrot)

How many instructions are really just microcode--bloat for the parrot
core? What's next after
{set,assign}{keyed_,}_{add,mul,sub,div,mod,cat,...}_{i,p,n}_{i,p,n}_{i,p
,n}? How many of these combinatoric instructions offer actual
performance advantages after code has been JITted? What's the cost of
having dozens of extra instructions to do things which could be done
through the straightforward combination of those you absolutely need in
order to obtain a solution with full generality? What's the relative
likelyhood of bugs with the increased opcode count? Why should a
container PMC author be concerned (ever) about implementing arithmetic
operators? Does the need for "magick PMCs" in a minimalized container
ISA speak to a larger problem? How many of the combinatoric instructions
are legitimately useful to a caller who needs full generality? Why
should ($a[$i] += $b;) or ($a[$i] = $b + $c;) get special optimizations,
but ($a[$i] = $b + $c + $d;) be penalized? How many languages are going
to bother with the AST magick to recognize that "="("[]"(a, x),
any("+","-","/",...)(m, n)) can be translated to a single opcode? How
will a PIR peephole optimizer be able to approach these operators when
the PMCs have been endowed with the ability to respond differently to
these extra operators? How would the optimizer handle it if the keyed
version winds up being slower than the non-keyed operation? How would
the optimizer handle it if keyed is faster for certain PMCs, but
non-keyed is faster for others? Is the RISC design philosophy not useful
when applied to a JITted virtual machine?

--

Gordon Henriksen
IT Manager
ICLUBcentral Inc.
gor...@iclub.com


Leopold Toetsch

unread,
Aug 15, 2003, 5:58:35 PM8/15/03
to Togos, perl6-i...@perl.org
Togos <chumps...@yahoo.com> wrote:

> ... a bit strange that there is a keyed version
> of 'add', anyway.

What is that? I'd tossed all (hopefully) of the math, boolean, and
logical keyed vtables, for which we never want to have opcodes. Did I
miss one?

Ah yes - docu needs updating, but Dan didn't put his "yes agreed" under
these patches, yet ;-)

leo

Leopold Toetsch

unread,
Aug 15, 2003, 6:27:33 PM8/15/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg <ben.go...@hotpop.com> wrote:

> ... I'd rather have two versions of add, one


> with set/create semantics and the other with assign/mutate semantics.

As already outlined, this leads to some kind of duplicating of our
opcodes, vtables, penalty on JIT implementators and so on. We (/me)
don't need this.

Its up to the HLL, how to program a CPU (virtual or not). You generate
a sequence of opcodes that do the Right Thing[1] (Fast)[2]. This may
imply to use a "assign", "clone", "set" or whatever opcode and an
"add" opcode, depending on the semantics or whatever the language
might need (references or values or ...)[3]

[1] there must be a minimal set of opcodes, that doesn't prohibit the
programmar ... If Something is really missing, we'll add some
wires to the (VM) hardware.
[2] If a sequence of ops is heavily used and some shortcut (micro
op) comes out to be a lot faster, we'll rewire the engine and
implement it in core.
[3] most of that stuff can be handled by the vtable implementing e.g.
the C<add> method. It can do everything with a (yes - existing)
destination PMC.

just my 2c

leo

Dan Sugalski

unread,
Aug 15, 2003, 9:53:09 PM8/15/03
to perl6-i...@perl.org

Right, because I don't agree.
--
Dan

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

Leopold Toetsch

unread,
Aug 16, 2003, 12:06:32 PM8/16/03
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:
> At 11:58 PM +0200 8/15/03, Leopold Toetsch wrote:
>>What is that? I'd tossed all (hopefully) of the math, boolean, and
>>logical keyed vtables,

>>Ah yes - docu needs updating, but Dan didn't put his "yes agreed" under
>>these patches, yet ;-)

> Right, because I don't agree.

I know that. But nothing has changed WRT multi-keyed ops since this
thread:

Date: Fri, 20 Sep 2002 11:12:18 +0200
From: Leopold Toetsch <l...@toetsch.at>
To: P6I <perl6-i...@perl.org>
Subject: [RFC] 2. Proposal for _keyed opcodes

You write there: "I don't mind the opcode explosion, honestly."

How long does it take to compile core_ops_cg.c with 60 times the opcode,
we now have?
What aboud cache locality?
What about final executable size (with speciall respect to embbeding
Parrot in some small devices)?

The only difference of my proposal and the mulit-keyed variants are 3
additional opcodes, which doesn't really count - we have a really fast
opcode dispatch:

/* OP _p_k _p_k_p_k =>
* set py, p_k
* set pz, p_k
* OP px, py, pz
* assign _p_k, px
*/
(Slightly updated version of the orig proposal)

> Dan

leo

Dan Sugalski

unread,
Aug 16, 2003, 12:24:58 PM8/16/03
to Gordon Henriksen, Perl6 Internals (parrot)
At 5:36 PM -0400 8/15/03, Gordon Henriksen wrote:
>Is the RISC design philosophy not useful
>when applied to a JITted virtual machine?

Skipping all the rest...

No. It isn't, and JITting doesn't have anything to do with this. The
issue is expressable semantics, and in languages with active data
(which encompasses a number of the languages we're interested in,
icluding perl, python, and ruby) not allowing direct access to
members of an aggregate makes doing things with that aggregate both
slower and far more convoluted in a number of ways.

Dan Sugalski

unread,
Aug 16, 2003, 2:59:02 PM8/16/03
to l...@toetsch.at, perl6-i...@perl.org
At 6:06 PM +0200 8/16/03, Leopold Toetsch wrote:
>Dan Sugalski <d...@sidhe.org> wrote:
>> At 11:58 PM +0200 8/15/03, Leopold Toetsch wrote:
>>>What is that? I'd tossed all (hopefully) of the math, boolean, and
>>>logical keyed vtables,
>
>>>Ah yes - docu needs updating, but Dan didn't put his "yes agreed" under
>>>these patches, yet ;-)
>
>> Right, because I don't agree.
>
>I know that. But nothing has changed WRT multi-keyed ops since this
>thread:
>
> Date: Fri, 20 Sep 2002 11:12:18 +0200
> From: Leopold Toetsch <l...@toetsch.at>
> To: P6I <perl6-i...@perl.org>
> Subject: [RFC] 2. Proposal for _keyed opcodes
>
>You write there: "I don't mind the opcode explosion, honestly."
>
>How long does it take to compile core_ops_cg.c with 60 times the opcode,
>we now have?

We'll only have double the number of ops (since we did decide, ages
ago, that if one PMC in an all-PMC operation was accessed via key
that they all would) so it's not a factor of 60 explosion. Even if it
was, I'm not worried about the time to compile parrot. That's
something that happens once, for any installation, so the compile
time's generally irrelevant. Yeah, it's a problem for us, but we can
deal.

>What aboud cache locality?

This won't generally be an issue--either the functions get used a
lot, in which case they'll be pinned in cache and work quickly, which
is what we want, or they'll be used very rarely and it won't matter,
since no matter what we do we'll be blowing cache. Also, in many
cases the non-key form would make cache locality worse, since it'll
involve creating PMCs for things that otherwise wouldn't have or need
them. (Plus the attendant runtime overhead to construct the new
temporary PMCs)

>What about final executable size (with speciall respect to embbeding
>Parrot in some small devices)?

That's definitely an issue, yes. This is something that someone
looking to embed parrot into small devices may want to look at
chopping out.

>The only difference of my proposal and the mulit-keyed variants are 3
>additional opcodes, which doesn't really count - we have a really fast
>opcode dispatch:

And semantic differences--don't forget those. They trump everything else, FWIW.

Leopold Toetsch

unread,
Aug 17, 2003, 6:45:34 AM8/17/03
to Dan Sugalski, perl6-i...@perl.org
Dan Sugalski <d...@sidhe.org> wrote:
> At 6:06 PM +0200 8/16/03, Leopold Toetsch wrote:
>>How long does it take to compile core_ops_cg.c with 60 times the opcode,
>>we now have?

> We'll only have double the number of ops (since we did decide, ages
> ago, that if one PMC in an all-PMC operation was accessed via key
> that they all would) so it's not a factor of 60 explosion.

To handle all cases with one additional opcode per operation means, that
we only have the general form with a Key PMC per argument. This requires
the construction of keys (even NULL keys?) for each argument.
Constructing these keys (albeit at load time) isn't free. Where we now
have an optimized keyed_int opocde, the 3-key variant needs a full PMC
key, just holding an immediate constant.

To handle all permutations of keyed/non-keyed, each of these vtables has
to check, if the associated argument key is NULL or not, which further
slows things down.

> ... Even if it


> was, I'm not worried about the time to compile parrot. That's
> something that happens once, for any installation, so the compile
> time's generally irrelevant. Yeah, it's a problem for us, but we can
> deal.

This "generally irrelevant" is going to hit me 20 times a day (or, if
we have the opcode explosion, a cron job is compiling Parrot during the
night - once) Can you provide an 8-way eServer 325 with plenty of RAM
or some such for developers?

>>What aboud cache locality?

> This won't generally be an issue--either the functions get used a
> lot,

What is the general usage of these ops? Where do you see this big
advantage of bloating the core?

>>What about final executable size (with speciall respect to embbeding
>>Parrot in some small devices)?

> That's definitely an issue, yes. This is something that someone
> looking to embed parrot into small devices may want to look at
> chopping out.

2 variants of our classes and ops? One with and one without multi-keyed?
Who is getting to manage that?

>>The only difference of my proposal and the mulit-keyed variants are 3
>>additional opcodes, which doesn't really count - we have a really fast
>>opcode dispatch:

> And semantic differences--don't forget those.

A keyed "add" vtable doesn't help to provide more semantics. The set vs
assign thread applies here too. On the contrary: to provide all
semantics you would need "add_set_p_k_p_k_p_k" and "add_assign_p_k_p_k_p_k"
and possibly "add_clone_p_k_p_k_p_k".

leo

Matt Fowles

unread,
Aug 17, 2003, 9:34:32 AM8/17/03
to Dan Sugalski, Gordon Henriksen, Perl6 Internals (parrot)
Dan Sugalski wrote:
> No. It isn't, and JITting doesn't have anything to do with this. The
> issue is expressable semantics, and in languages with active data (which
> encompasses a number of the languages we're interested in, icluding
> perl, python, and ruby) not allowing direct access to members of an
> aggregate makes doing things with that aggregate both slower and far
> more convoluted in a number of ways.

Would you please give an example of active data and why one needs the
keyed ops to deal with it?

I for one am too heavily entenched in static languages and do not
honestly know.

Matt

Sean O'Rourke

unread,
Aug 17, 2003, 11:20:19 AM8/17/03
to l...@toetsch.at, Dan Sugalski, perl6-i...@perl.org
Leopold Toetsch <l...@toetsch.at> writes:

> Dan Sugalski <d...@sidhe.org> wrote:
>> And semantic differences--don't forget those.
>
> A keyed "add" vtable doesn't help to provide more semantics. The set vs
> assign thread applies here too. On the contrary: to provide all
> semantics you would need "add_set_p_k_p_k_p_k" and "add_assign_p_k_p_k_p_k"
> and possibly "add_clone_p_k_p_k_p_k".

If I'm on target, what Dan's trying to avoid is having to create
magic proxy objects for autovivification. If this is all we want out
of keyed ops, then we can get it more cheaply by only adding keyed
variants for the _out_ parameters of each op. That way the standard
get_*_keyed ops can either return NULL, return a new PMC without
insertion, or insert and return a new PMC for read parameters, while
the *_pk_*_* ops can do "the right thing" with respect to
autovivification. No temporary keys or null keys need be created.

I think this covers the semantics. Beyond that, it's just a matter
of performance -- extra opcodes vs. extra ops -- that we can fight
out in the benchmark arena.

/s

Benjamin Goldberg

unread,
Aug 17, 2003, 6:28:24 PM8/17/03
to perl6-i...@perl.org

Hmm... I just thought of something. Since 'set' semantics can be easily
simulated when we have only ops for 'assign' semantics, maybe imcc
itself could do this for us.

That is, by default,


$P0 = $P1 + $P2

will be translated by imcc into
add $P0, $P1, $P2
but, if some command is given to imcc, then it will be translated into
new $P0, .PerlUndef
add $P0, $P1, $P2
Or, instead of PerlUndef, the command to change to set semantics would
indicate what to put into $P0 prior to using it as the target of an add
(so we can initialize $P0 with any of PerlInt, PerlNum, BigInt,
BigFloat, BigRat, ContinuedFraction, etc.)

Luke Palmer

unread,
Aug 17, 2003, 8:02:53 PM8/17/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg writes:
> Hmm... I just thought of something. Since 'set' semantics can be easily
> simulated when we have only ops for 'assign' semantics, maybe imcc
> itself could do this for us.
>
> That is, by default,
> $P0 = $P1 + $P2
> will be translated by imcc into
> add $P0, $P1, $P2
> but, if some command is given to imcc, then it will be translated into
> new $P0, .PerlUndef
> add $P0, $P1, $P2
> Or, instead of PerlUndef, the command to change to set semantics would
> indicate what to put into $P0 prior to using it as the target of an add
> (so we can initialize $P0 with any of PerlInt, PerlNum, BigInt,
> BigFloat, BigRat, ContinuedFraction, etc.)

Hmm, I wonder whether this is the right approach. What about operator
overloading? Let's say Python overloads + (can it do that?) and passes
that variable to Perl. If Perl creates a .PerlUndef and assigns to it,
you lose the overloaded semantics.

I don't know... I think using vtable multimethods would be the best way
to go wrt these things.

Luke

Benjamin Goldberg

unread,
Aug 17, 2003, 9:12:03 PM8/17/03
to perl6-i...@perl.org
Luke Palmer wrote:
>
> Benjamin Goldberg writes:
> > Hmm... I just thought of something. Since 'set' semantics can be
> > easily simulated when we have only ops for 'assign' semantics, maybe
> > imcc itself could do this for us.
> >
> > That is, by default,
> > $P0 = $P1 + $P2
> > will be translated by imcc into
> > add $P0, $P1, $P2
> > but, if some command is given to imcc, then it will be translated into
> > new $P0, .PerlUndef
> > add $P0, $P1, $P2
> > Or, instead of PerlUndef, the command to change to set semantics would
> > indicate what to put into $P0 prior to using it as the target of an
> > add (so we can initialize $P0 with any of PerlInt, PerlNum, BigInt,
> > BigFloat, BigRat, ContinuedFraction, etc.)
>
> Hmm, I wonder whether this is the right approach. What about operator
> overloading? Let's say Python overloads + (can it do that?) and passes
> that variable to Perl. If Perl creates a .PerlUndef and assigns to it,
> you lose the overloaded semantics.

Eh? I would think that if Python overloads +, then that means it's
creating a PMC with a vtable->add different from a "normal" addition
operation. If we "assign" that pmc to a pmc which was PerlUndef, then
the vtable (pointer) gets assigned, too. Doesn't it?

Unless you mean, "let's say Python makes a pmc with an unusual vtable,
and overloads vtable->add in such a way that it expects it's target to
be a <something> pmc, but instead, we pass in a PerlUndef." If
vtable->add doesn't bother checking that the target isn't the right
type, then it's broken. It should either throw an exception if it's not
what's wanted, or else mutate/promote that PerlUndef to the right type.

> I don't know... I think using vtable multimethods would be the best way
> to go wrt these things.

Umm, I didn't think that I was suggesting something else -- I was
suggesting merely that imcc can be told to produce to pretend that +, -,
and other mathematical operations have "set" semantics, and have it
produce parrot ops to support that pretense. Within parrot, the "add"
op would still be the same as it always has been (merely calling the
vtable->add method). And I'm not suggesting that the vtables be
changed. The only difference would be, *if* that command had been given
to imcc, then any "add" op in the bytecode, which had originally been
spelled as "+" in the pir code, would be preceded by a "new" op (put
there by imcc).

Michal Wallace

unread,
Aug 17, 2003, 11:51:33 PM8/17/03
to Luke Palmer, Benjamin Goldberg, perl6-i...@perl.org
On Sun, 17 Aug 2003, Luke Palmer wrote:

> Benjamin Goldberg writes:
> > Hmm... I just thought of something. Since 'set' semantics can be easily
> > simulated when we have only ops for 'assign' semantics, maybe imcc
> > itself could do this for us.
> >
> > That is, by default,
> > $P0 = $P1 + $P2
> > will be translated by imcc into
> > add $P0, $P1, $P2
> > but, if some command is given to imcc, then it will be translated into
> > new $P0, .PerlUndef
> > add $P0, $P1, $P2
> > Or, instead of PerlUndef, the command to change to set semantics would
> > indicate what to put into $P0 prior to using it as the target of an add
> > (so we can initialize $P0 with any of PerlInt, PerlNum, BigInt,
> > BigFloat, BigRat, ContinuedFraction, etc.)
>
> Hmm, I wonder whether this is the right approach. What about operator
> overloading? Let's say Python overloads + (can it do that?) and passes
> that variable to Perl. If Perl creates a .PerlUndef and assigns to it,
> you lose the overloaded semantics.

Sure, you can do that in python:

>>> class Add:
... def __add__(self, other):
... print "we don't need no steenkin", other
...
>>> x = Add()
>>> x + 1
we don't need no steenkin 1

But if you know the types of the objects, it makes sense
to do the change Benjamin is talking about. So this probably
ought to be done at the AST code generation level.

Leo sent me a patch that does some basic type inference in pirate and
figures out when not to generate the extra PerlUndef. It's in the
method binaryExpression. (It's just not used yet because at the time
it wasn't compatible with the way I was handling expressions; I've
been trying to get my code to work his way, but didn't go back and
check whether it worked yet.)

Sincerely,

Michal J Wallace
Sabren Enterprises, Inc.
-------------------------------------
contact: mic...@sabren.com
hosting: http://www.cornerhost.com/
my site: http://www.withoutane.com/
--------------------------------------


Leopold Toetsch

unread,
Aug 18, 2003, 3:36:40 AM8/18/03
to Sean O'Rourke, perl6-i...@perl.org
Sean O'Rourke <soro...@cs.ucsd.edu> wrote:
> Leopold Toetsch <l...@toetsch.at> writes:

>> A keyed "add" vtable doesn't help to provide more semantics. The set vs
>> assign thread applies here too. On the contrary: to provide all
>> semantics you would need "add_set_p_k_p_k_p_k" and "add_assign_p_k_p_k_p_k"
>> and possibly "add_clone_p_k_p_k_p_k".

> If I'm on target, what Dan's trying to avoid is having to create
> magic proxy objects for autovivification. If this is all we want out
> of keyed ops, then we can get it more cheaply by only adding keyed
> variants for the _out_ parameters of each op.

In one of my proposals I had something like this:

key k1, P[] # add P[], P[], P[]
key k2, P[]
key_dest k3, P[]
add k3, k1, k2

The source C<key> opcodes are basically like C<set> (albeit they could
use a special key register (pointer), so no PRegs are globbered). The
C<key_dest> does whatever is necessary to autovivify the LHS. This
sequence is autogenerated inside the assembler (like
imcc.y:multi_keyed()) now. JIT can emit only the vtable calls and have
e.g. the pointers in processor registers. So JIT doesn't have any
overhead, for CGP there are 3 additional jumps.

There are no tests needed, if a key isn't present (the Ckey> instruction
is omitted then). There is no overhead for e.g. overloading add - this
would need code duplication if there is add_keyed. KISS.

> /s

leo

0 new messages