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

Reply via email to