1) Mixed infix operations

Opcodes that take one PMC and one native type argument, like:

  op add(in PMC, in PMC, in INT)

should probably become plain vtable methods again. There isn't much "multi" in the dispatch as the dispatch degenerates to a search in the class of the left argument's MRO. OTOH this may have some implications as Perl6 is treating these as normal multi subs. Maybe we need just a fake "int" class for the sake of MMD.

2) all vtable and MMD functions should be NCI methods

The current static lookup in the MMD_table doesn't work correctly especially, when methods are overloaded. While the C<mmdvtregister> opcode can be used to install a new method for a class pair, it is not usable to get the inheritance right. If e.g. a method is inserted dynamically in the middle of a classes MRO, all possible classes would need adjustment. I don't see a way to do this correctly.

It's much simpler to just do a dynamic lookup once and cache the result inside the PIC (polymorphic inline cache) [1].

3) tcl and py classes should inherit all common functions

Currently too much code is just cut&pasted. Removing these duplicates also simplifies 5)

4) internal opcode change (PMC ops only)

The PASM/PIR compilers convert the user visible opcode

  d = l + r   # or add d, l, r

to a new opcode:

  infix "__add", d, l, r

Rational: all these infix opcodes with PMCs do the same thing: locate the method and either call the C function (inside the NCI method) or run a piece of user code. We therefore need just one opcode for all these methods.

This also allows easily extensions like:

  infix "__hyper_add", d, l, r

5) infix method signature change:

  METHOD PMC* add( [INTERP, SELF,] PMC* rhs, PMC ´*dest) {
    if (!dest)
       dest = pmc_new(INTERP, SELF->vtable->base_type);
    ...
    return dest;
  }

If the destination PMC is passed in, it's used else a new PMC of an appropriate type is created.

We need this basically for 4 reasons:

a) Python
All Python scalars are immutable. The current scheme to morph the destination PMC to the type of the result is basically wrong. All Python operations on scalars always return a new result PMC.


b) operations with temporary PMCs

   $P0 = a + b

the current canonical sequence is:

  $P0 = new Undef
  $P0 = a + b

this takes two opcodes and the morphing is a slow and bulky operation see src/pmc.c:pmc_reuse().

c) overridden infix methods return a new PMC, there is no destination to pass in.

  multi infix<+>(Int $l, Int $r) { ... return $d; }

or

  def myadd(self, r):
     return ..
  class I(object):
     __add__ = myadd

d) singleton return values. You can't morph a destination to a singletion as this would need the change of the PMC's address.

6) new user-visible opcodes that create new destination PMCs:

  op n_add(out PMC, in PMC, in PMC)
           ^^^^^^^
or

  d = n+  l, r      # or some such

Within the pragma ".use n_operators" the plain:

  d = l + r

also translates to "n_add"

The internal opcode is:

  infix_n "__add", d, l, r

7) separate inplace methods

Opcodes like:

  d += r    # add d, r

are currently using the normal add method with the destination set to SELF. This is suboptimal, especially, when the destination PMC is morphed to a different type (e.g. due to bigint promotion) which destroys the value of SELF.

It's just cleaner to have distinct inplace methods, it's very likely also needed anyway, as method overloading would not work if the inplace operations are the same.

Therefore we have:

  infix "__i_add", d, r

and in *.pmc

  METHOD void i_add( [INTERP, SELF, ] PMC* r) {...}


[1] See also:

Subject: "PIC for more MOPS but not only"
Subject: "PIC again"

Comments welcome,
leo



Reply via email to