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

Method calling

4 views
Skip to first unread message

Dan Sugalski

unread,
May 30, 2003, 8:12:34 AM5/30/03
to perl6-i...@perl.org
Nope, not objects, but at least it's partway there. This bit is about
making method calls.

We've two issues on the front end. First, we need to be able to get a
method PMC from an object, in those cases where we want to save that
for later, and second we need to call methods. So, here's how we're
going to do it.

We add three ops, findmeth, callmeth and callmethcc. (The latter just
automatically takes a continuation for the following op and stashes
it in P2, while the former assumes one has been put in P2 already)
They all find a method PMC based on the object and method name
already loaded. The call ops then call
that PMC's invoke vtable entry, which acts as normal.

So the sequence is:

P0 = P2->find_method(interpreter, P2);

and for the call ops, then:

dest = P0->invoke(interpreter, P0, expr NEXT());
goto ADDRESS(dest);

The returned method PMC handles anything fancy--multimethod dispatch
and whatnot. We need base engine support for MMD and method caching,
amongst other things, but we can get to that later.

The method PMC returned by findmeth can be invoked as a regular
subroutine, so we're fine there. I'm not, at the moment, leaning
towards a separate "invoke this pre-fetched PMC sub as a method" op,
since we're not passing on any information as to how a PMC is invoked
outside of there being an object in P2, but I can be convinced
otherwise if we can make a good case for languages actually using
that information.

I'm thinking at this point, since we need to add the call and callcc
ops as it is, that we should probably have tail versions of all these
ops, which just puts the various bits from the continuation in P1
back before making the call, but we can talk about that later if we
want.
--
Dan

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

Jonathan Sillito

unread,
May 30, 2003, 12:20:23 PM5/30/03
to Dan Sugalski, perl6-i...@perl.org
Dan,

Why is there a callmeth op? Can't we just use a regular invoke as with other
subs? Or does the callmeth op do both a find and invoke as an optimization?

Jonathan Sillito

Dan Sugalski

unread,
May 30, 2003, 12:29:16 PM5/30/03
to Jonathan Sillito, perl6-i...@perl.org
At 9:20 AM -0700 5/30/03, Jonathan Sillito wrote:
>Dan,
>
>Why is there a callmeth op? Can't we just use a regular invoke as with other
>subs? Or does the callmeth op do both a find and invoke as an optimization?

It does a find and invoke, and the callmethcc does a find,
cc-capture, and invoke.

I'm not sure if there'll be much win, but it is more information
available to analyzers, so they *know* that this is a method call
rather than a random sub call.

I figure the vast majority of the method calls most languages make
will be either callmeth or callmethcc calls, so it makes sense to wad
up the functionality in a single op.

Jonathan Sillito

unread,
May 30, 2003, 12:38:26 PM5/30/03
to Dan Sugalski, perl6-i...@perl.org
Thanks Dan. One little picky thing what is the naming convention for ops?

find_method (currently in core.ops),
call_method, and
call_method_cc (or callcc_method?)

or

findmeth,
callmeth, and
callmethcc (or callccmeth?)

or

findmethod,
callmethod, and
callmethodcc (or callccmethod?)

Also, should the current 'invoke' op be changed to 'call'? I don't care
exactly what the names are, but consistency is nice.

Jonathan

Nicholas Clark

unread,
May 30, 2003, 3:30:07 PM5/30/03
to Dan Sugalski, perl6-i...@perl.org
I'm not sure if I'm asking a stupid question here, but:

On Fri, May 30, 2003 at 08:12:34AM -0400, Dan Sugalski wrote:

> We add three ops, findmeth, callmeth and callmethcc. (The latter just
> automatically takes a continuation for the following op and stashes
> it in P2, while the former assumes one has been put in P2 already)

You say P2 is a continuation

> They all find a method PMC based on the object and method name
> already loaded. The call ops then call
> that PMC's invoke vtable entry, which acts as normal.
>
> So the sequence is:
>
> P0 = P2->find_method(interpreter, P2);

But you seem to be using P2 like it's the object
I'm confused.
Surely there are 3 things to play with - object, method name and
continuation to "return" to? If so, are they P0, P1 and P2 before these
ops?

Nicholas Clark

Jonathan Sillito

unread,
May 30, 2003, 3:45:32 PM5/30/03
to Nicholas Clark, Dan Sugalski, perl6-i...@perl.org
Calling convention says that before a sub/method is invoked:

- P0 Holds the object representing the subroutine.

- P1 Holds the continuation for the caller, assuming this sub was called
with callcc. Otherwise NULL.

- P2 Holds the object the sub was called on. (For method calls)

Or put another way P2 is the receiver object. So I guess this was a typo;
the continuation should be stashed in P1.

While I am at it, here is an **untested** implementation of the call_method
op. I don't want to send a patch since I am not sure how to test this yet
(since no PMCs respond to find_method).

op call_method(in STR) {
opcode_t *dest;
/* assumes receiver object is in P2 */
PMC* receiver = interpreter->ctx.pmc_reg.registers[2];
PMC* method = receiver->vtable->find_method(interpreter, receiver, $1);

/* calling convention says that method should be in P0 and name in S0 */
interpreter->ctx.pmc_reg.registers[0] = method;
interpreted->ctx.string_reg.registers[0] = $1;

dest = (opcode_t *)method->vtable->invoke(interpreter, method, expr
NEXT());
goto ADDRESS(dest);
}

op call_method(in PMC, in STR) {
opcode_t *dest;
PMC* method = $1->vtable->find_method(interpreter, $1, $2);

/* calling convention says that receiver should be in P2 and method in P0
*/
interpreter->ctx.pmc_reg.registers[0] = method;
interpreter->ctx.pmc_reg.registers[2] = $1;
interpreted->ctx.string_reg.registers[0] = $2;

dest = (opcode_t *)method->vtable->invoke(interpreter, method, expr
NEXT());
goto ADDRESS(dest);
}

--
Jonathan Sillito

Mitchell N Charity

unread,
May 30, 2003, 7:42:54 PM5/30/03
to perl6-i...@perl.org
Eeep.

I was struck by this recent code fragment


/* calling convention says that receiver should be in P2 and method in P0 */
interpreter->ctx.pmc_reg.registers[0] = method;
interpreter->ctx.pmc_reg.registers[2] = $1;

interpreter->ctx.string_reg.registers[0] = $2;
which might be rewritten as
P0 = method;
P2 = $1;
S0 = $2;
with just a little bit of macroness.


So I wandered off to look at the state of
interpreter->ctx.FOO_reg.registers
usage in parrot.


#### INT_REG ####

A macro set
# define INT_REG(x) interpreter->ctx.int_reg.registers[x]
# define NUM_REG(x) interpreter->ctx.num_reg.registers[x]
# define STR_REG(x) interpreter->ctx.string_reg.registers[x]
# define PMC_REG(x) interpreter->ctx.pmc_reg.registers[x]
is identically defined and used within two files,
jit/i386/jit_emit.h
and
build_nativecall.pl
and thus also its generated
nci.c

jit/i386/jit_emit.h also has one similar but unmacroed access.


#### IREG ####

A second macro set is variously defined as
#define IREG(i) interpreter->ctx.int_reg.registers[i]
#define NREG(i) interpreter->ctx.num_reg.registers[i]
#define PREG(i) interpreter->ctx.pmc_reg.registers[i]
#define SREG(i) interpreter->ctx.string_reg.registers[i]
or
#define IREG(i) interpreter->ctx.int_reg.registers[cur_opcode[i]]
#define NREG(i) interpreter->ctx.num_reg.registers[cur_opcode[i]]
#define PREG(i) interpreter->ctx.pmc_reg.registers[cur_opcode[i]]
#define SREG(i) interpreter->ctx.string_reg.registers[cur_opcode[i]]
or
#define IREG(i) interpreter->ctx.int_reg.registers[jit_info->cur_op[i]]
#define NREG(i) interpreter->ctx.num_reg.registers[jit_info->cur_op[i]]
#define PREG(i) interpreter->ctx.pmc_reg.registers[jit_info->cur_op[i]]
#define SREG(i) interpreter->ctx.string_reg.registers[jit_info->cur_op[i]]
by
lib/Parrot/OpTrans/Compiled.pm
lib/Parrot/OpTrans/CGoto.pm and lib/Parrot/OpTrans/C.pm
jit2h.pl
respectively, and thus as
#define IREG(i) interpreter->ctx.int_reg.registers[cur_opcode[i]]
and
#define IREG(i) interpreter->ctx.int_reg.registers[jit_info->cur_op[i]]
in
core_ops.c and core_ops_cg.c
jit_cpu.c
respectively,
and used in those three files.

Hmm... so is Compiled.pm maybe using a the same names for a different concept?

#### unmacroed ####

Unmacroed usage is done about 72 times (or 81 including generated files)
in 18 (or 26) files.
classes/compiler.pmc
core.ops
debug.c
dod.c
embed.c
interpreter.c
jit/alpha/jit_emit.h
jit/arm/jit_emit.h
jit.c
jit_debug.c
jit/i386/jit_emit.h
jit/ppc/jit_emit.h
jit/sun4/jit_emit.h
key.c
languages/imcc/optimizer.c
pbc2c.pl
register.c
trace.c
and
classes/compiler.c
core_ops.c
core_ops_cg.c
core_ops_cgp.c
core_ops_prederef.c
core_ops_switch.c
include/parrot/jit_emit.h
lib/Parrot/OpLib/core.pm

Lots'o cut-and-paste.

#### variants ####

->registers is used 4 times in debug.c

And 4 times bogusly in ops2cgc.pl, like
'i' => "interpreter->ctx.int_reg->registers[cur_opcode[%ld]]"
Perhaps this should be fixed?

Non-"ctx" .registers are used 7 times in 2 files,
dod.c
register.c
Specifically,
dod.c: if (cur_chunk->PReg[j].registers[i]) {
dod.c: (PObj *)cur_chunk->PReg[j].registers[i]);
dod.c: Buffer *reg = (Buffer *)cur_chunk->SReg[j].registers[i];
register.c: &top->IReg[top->used].registers[NUM_REGISTERS/2],
register.c: &top->SReg[top->used].registers[NUM_REGISTERS/2],
register.c: &top->NReg[top->used].registers[NUM_REGISTERS/2],
register.c: &top->PReg[top->used].registers[NUM_REGISTERS/2],

Non-"interpreter" ctx registers are used in 12 times in 3 files,
jit/sun4/jit_emit.h
languages/imcc/optimizer.c
method_util.c
Specifically,
jit/sun4/jit_emit.h: #define Parrot_jit_regbase_ptr(i) &((i)->ctx.int_reg.registers[0])
languages/imcc/optimizer.c: sprintf(b, INTVAL_FMT, interp->ctx.int_reg.registers[0]);
languages/imcc/optimizer.c: sprintf(b, fmt, interp->ctx.num_reg.registers[0]);
method_util.c: interp->ctx.int_reg.registers[0] = 0; /* no prototype */
method_util.c: interp->ctx.int_reg.registers[1] = argc;
method_util.c: INTVAL nret = interp->ctx.int_reg.registers[1];
method_util.c: interp->ctx.int_reg.registers[0] = 1; /* with proto */
method_util.c: push_these(npush, interp, interp->ctx.int_reg.registers, intc, intv[i],
method_util.c: push_these(npush, interp, interp->ctx.num_reg.registers, numc, numv[i],
method_util.c: push_these(npush, interp, interp->ctx.string_reg.registers, strc,
method_util.c: push_these(npush, interp, interp->ctx.pmc_reg.registers, pmcc,
method_util.c: interp->ctx.int_reg.registers[1] = npush;
Though only 8 of the 12 times are individual register accesses.
Of these 8, the 5 in method_util.c result from its saying "interp"
rather than "interpreter" (actually, it uses both conventions, sigh).

Perhaps method_util.c should be "interpreter"-ized?

ctx.FOO_reg and FOO_reg.registers are used whole 34 times in 6 files.
debug.c
debug.ops
method_util.c
ops2cgc.pl
register.c
trace.c

There are about 166 uses of interpreter->ctx. which are not FOO_reg.registers.

#### so what? ####

The vast majority of register accesses (~90%) are simply cut-and-pastes of 4
interpreter->ctx.FOO_reg.registers[BAR]
strings.

Register access is a high profile user concept.

It looks like register access is a plausible place to make an
abstraction cut.

The only(?) downside of doing so is decoupling register access
interpreter->ctx.FOO_reg.registers[BAR]
from the various
interpreter->ctx.SOMETHING_ELSE
expressions.
But, this decoupling has _already_ occurred in several files.

So...

I suggest existing register access be replaced with a new macro set
#define REG_INT(x) interpreter->ctx.int_reg.registers[x]
#define REG_NUM(x) interpreter->ctx.num_reg.registers[x]
#define REG_STR(x) interpreter->ctx.string_reg.registers[x]
#define REG_PMC(x) interpreter->ctx.pmc_reg.registers[x]

(Then jit/i386/jit_emit.h and build_nativecall.pl can be synced by
flopping FOO_REG's to REG_FOO's.)

I suggest REG_PMC(1) is clearer than PMC_REG(1),
as it places the most variable information together.
Like P1, or CLUTTER_P(1), rather than P_CLUTTER(1).

At a minimum, it would be nice for some register access macro set to
be globally available.

While
REG_PMC(0) = method;
REG_PMC(2) = $1;
REG_STR(0) = $2;
is not as clear as
P0 = method;
P2 = $1;
S0 = $2;
it sure beats


interpreter->ctx.pmc_reg.registers[0] = method;
interpreter->ctx.pmc_reg.registers[2] = $1;

interpreter->ctx.string_reg.registers[0] = $2;

No?

Mitchell

Bryan C. Warnock

unread,
May 30, 2003, 10:22:30 PM5/30/03
to mcha...@vendian.org, perl6-i...@perl.org
On Fri, 2003-05-30 at 19:42, Mitchell N Charity wrote:
> Eeep.

{snip snip}

No.

Ha ha, just kidding, of course. I'm all for it, but given my record
today, that might be an imminent sign of its rejection.

--
Bryan C. Warnock
bwarnock@(gtemail.net|raba.com)

Sean O'Rourke

unread,
May 30, 2003, 11:08:17 PM5/30/03
to Bryan C. Warnock, mcha...@vendian.org, perl6-i...@perl.org
On 30 May 2003, Bryan C. Warnock wrote:
> Ha ha, just kidding, of course. I'm all for it, but given my record
> today, that might be an imminent sign of its rejection.

Or, given your historical record, you may have just killed the thread ;).

/s

Leopold Toetsch

unread,
May 31, 2003, 11:54:33 AM5/31/03
to mcha...@vendian.org, perl6-i...@perl.org
Mitchell N Charity <mcha...@vendian.org> wrote:

> At a minimum, it would be nice for some register access macro set to
> be globally available.

> REG_PMC(0) = method;

Yep. Seems reasonable. The macros would improve readability IMHO

> Mitchell

leo

Dan Sugalski

unread,
May 31, 2003, 4:21:09 PM5/31/03
to Jonathan Sillito, perl6-i...@perl.org
At 9:38 AM -0700 5/30/03, Jonathan Sillito wrote:
>Thanks Dan. One little picky thing what is the naming convention for ops?

Good question--there really isn't one. I've been trying to avoid
underscores in names, since it was inconvenient when the assembler
was trying to figure out if you were using a base name or an expanded
name, but that's about it.

I'm up for suggestions.

>Also, should the current 'invoke' op be changed to 'call'? I don't care
>exactly what the names are, but consistency is nice.

Yeah, I think so. I originally figured that invoke would be different
than call, but at this point they aren't, and I don't see any reason
they would be. So we might as well rename invoke and be done with it.

Dan Sugalski

unread,
May 31, 2003, 4:23:09 PM5/31/03
to Nicholas Clark, perl6-i...@perl.org
At 8:30 PM +0100 5/30/03, Nicholas Clark wrote:
>I'm not sure if I'm asking a stupid question here, but:
>
>On Fri, May 30, 2003 at 08:12:34AM -0400, Dan Sugalski wrote:
>
>You say P2 is a continuation

[snip]

>But you seem to be using P2 like it's the object
>I'm confused.

No, I was. There are three slots that're important here:

P0 - The sub PMC
P1 - The return continuation
P2 - The object

I just typo'd the example code.

Dan Sugalski

unread,
May 31, 2003, 4:24:15 PM5/31/03
to l...@toetsch.at, mcha...@vendian.org, perl6-i...@perl.org

Absolutely. Works for me, so let's do it.

Piers Cawley

unread,
Jun 2, 2003, 11:06:57 AM6/2/03
to Sean O'Rourke, Bryan C. Warnock, mcha...@vendian.org, perl6-i...@perl.org

Threads die when one of two things happen:

1. Someone compares someone/thing/group unfavourably to Hitler. At
this point the signal/noise ratio of the thread approaches 0. This is
known as Godwin's Law.

2. Bryan C. Warnock says something, at which point everyone else in
the thread declines to comment, helping to increase Bryan's level
of paranoia whilst leaving him forever stuck on the horns of his
own Dilemma.

Personally, whilst 2. is potentially funny, I don't think it's
necessarily a good idea. It's a bit like the whole "I know, when John
Glenn lands on the shuttle, everyone should be wearing monkey
masks..." thing.

--
Piers

Robert Spier

unread,
Jun 1, 2003, 10:05:34 PM6/1/03
to perl6-i...@perl.org
> Yep. Seems reasonable. The macros would improve readability IMHO

As long as they don't impair debugability.

(I make a reference to perl5's macros containing macros, which do
different things based on other macros, and other fun things. It's a
fine line.)

-R


0 new messages