Version 2 (modified by Austin_Hastings, 5 years ago)


L1 - Level One Microcode for PVM

The L1 project will explore a way of implementing a core set of fundamental operations that can be used to construct higher-level functionality, including what are now thought of as PVM opcodes.

The L1 Recap describes the initial vision.

Thought Experiment

Consider writing a method for a PMC. Currently, they are written in a C-like language that is preprocessed by pmc2c (a perl script). The pmc2c preprocessor inserts code into each method body to handle interacting with the interpreter. This avoids having this boilerplate code in each function, which is convenient because it can be larger than the actual method code sometimes. But much of that boilerplate code is useless, or infrequently needed, or could and should be refactored outside the body of the 'method' itself. For example, each method specifies its parameters, and what register types it wants the parameters in. Why? The C doesn't use registers, but if the method were to call some other PIR, and that PIR needed to walk back up the call stack (for debugging, or an exception, or something far more sinister), the record has to exist even for "non-PIR" stack frames. Similarly, if the C wants to throw a resumable exception, or pass a return continuation to some other code, there has to be a certain amount of stuff set up. The boilerplate is doing some or all of that work now. But much of that could be coded a different way. Suppose instead of a big C function with a lot of boilerplate at the top (and bottom, and middle, and wherever pmc2c decides it's necessary...) there was a smaller data structure.

Let's say that each method is actually a PmcMethod object, and it has an "invoke" method. Most of the time, do something like:

    $P0 = get_method "foo"
    $P1 = make_args($P0, ...) # I don't know, some registers?
    make_stack_frame $P0, $P1
    $P2 = $P0.invoke()

    unless_exception $P2 goto no_exception

    $P3 = get_current_stack_frame

    $I0 = $P3.can_handle_exception($P2)
    if $I0 goto handle_exception

    chain_exception $P2

    # Lookup handler, etc.

    get_return_results $P2

Of course, sometimes you do need there to be a stack frame in the interp. So the make_stack_frame could just create some fake data, and leave a pointer for the off chance that someone needs to hydrate the real thing. Likewise, exception processing might need some special processing. So the downside here is that there are a lot more ops. And the ops look funny. They don't look like "add two numbers" or "call this function". The upside is that each of these ops is a replacement for some code that already exists in the current pmc2c boilerplate, and if things are done right most of those ops won't get executed (making the actual code that gets executed shorter, and contain fewer calls to malloc).