Leopold Toetsch wrote: > 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.
Agreed. >> 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. I'm not as sure as you appear to be, but we can worry about this later. >>> # 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: With that clarification, I agree in principle. > - try to find a method, else > - check attributes > - call __get__, if it is user provided > > (or whatever order CPython actually uses). What's in dynclass/pyclass.pmc matches Python's semantics closer. > The return value is a callable sub. More precisely: a curried function call. This is an important distinction; to see why, see below. >> 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. Note that you would then be caching the results of a curried function call. This result depends not only on the method string, but also on the particular object upon which it was invoked. > - use a PIC [1] (which is tied to the opcode) and cache > the final resut of find_method (or any similar lookup) Again, the results will depends on the object. > - run the invoked Sub inside the same run loop - specifically > not within Parrot_run_meth_fromc_args I don't understand this. (Note: it may not be important to this discussion that I do understand this - all that is important to me is that it works, and somehow I doubt that Parrot_run_meth_fromc_args cares whether a given function is curried or not). > [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). The above works because PyInt is a constant. It probably can be extended to handle things that seem unlikely to change very rapidly. But the combination of decisions on how to handle the passing of the "self" parameter to a method, keeping find_method and invoke separated at the VTABLE level, and the semantics of Python make the notion of caching the results of find_method problematic. >> - Sam Ruby > > leo - Sam Ruby