Sam Ruby wrote:
Leopold Toetsch wrote:
A stripped down PIR-only, pythonless translation is below.
(example: classes aren't global in Python),
Yes, of course. The stripped down means essential the absence of any
lexical handlings. But as you say, this doesn't matter for these sub and
method calls.
Note that Python provides __get__ methods for you on every function and
method, i.e., it is *not* optional, getattribute will always succeed.
That's fine. But if it's not overriden by user code, you exactly know
what it is doing. Therefore you can just emulate it, I think.
# print foo.f(2)
# emulate python find_name, which checks attributes too
push_eh m_nf
$P0 = foo."f"(2)
clear_eh
goto m_f
m_nf:
# getattribute would also check if __get__ is there
$P1 = getattribute foo, "f"
$P0 = foo.$P1(2)
m_f:
Note that the code above would need to be emitted for *every* method
call,
Above snippet should not be emitted for a method call, it just emulates
it in PIR. It should demonstrate how Python's find_method() could be
implemented:
- try to find a method, else
- check attributes
- call __get__, if it is user provided
(or whatever order CPython actually uses).
The return value is a callable sub.
Ideally, callmethodcc would result in one vtable call to enable
interesting optimizations for those that care to provide it. The
default implementation for this vtable entry could be a call to
find_method and invoke.
The interesting optimizations are:
- cache the find_method in the global method cache.
This happens already, if the method string is constant.
- use a PIC [1] (which is tied to the opcode) and cache
the final resut of find_method (or any similar lookup)
- run the invoked Sub inside the same run loop - specifically
not within Parrot_run_meth_fromc_args
[1] polymorphic inline cache
The important thing is that the method lookup and the subroutine
invocation happens from inside the runloop. Run cores that allow
rewriting of opcodes (prederefed and JIT) can insert faster equivalent
opcodes in these cases.
Have a look at src/pic.c (which doesn't implement a lot - it's more a
proof of concept now). Anyway here is an example that is implemented:
new P0, "PyInt" # new_p_sc
When this opcodes gets execute the first time, the type number of the
class is looked up and then the opcode is replaced with the faster variant:
new P0, 89 # new_p_ic - arbitrary number for PyInt
The same scheme can be applied for all these opcodes that consist of any
kind of a lookup (MMD, methods, attributes, ...) and some further
action. The lookup is done once at runtime (and again only after cache
invalidation).
- Sam Ruby
leo