Skip to content

Register instructions and ops in the instruction definition DSL #521

@gvanrossum

Description

@gvanrossum

It's pretty straightforward to add register instructions to the DSL. We can allow

register inst(NAME, (arg1, arg2 -- result)) {
    <code>
}

which generates code like this:

{
    PyObject *arg1 = REG(oparg1);
    PyObject *arg2 = REG(oparg2);
    PyObject *result;
    <code>
    REG(oparg3) = result;
}

There's an obvious restriction that there can be at most three I/O effects altogether (because there are only three opargs).

But what should we do for micro-ops? In the stack VM, the effect of two micro-ops is easily combined into the effect of a single instruction by simulating the stack effects in the generator: each input effect is a POP, each output effect is a PUSH. However, this doesn't work the same way for registers. E.g. if we had two register micro-ops,

register op(X, (arg1 -- res1)) { ... }
register op(Y, (arg2 --)) { ... }
macro(Z) = X + Y;

what should the register effect of Z be? I guess this could be (arg1, arg2 -- res1). But how can we pass info from one micro-op into the next? In the stack world the first op can PUSH something that the second op will POP, and since the PUSH and POP cancel each other out these are not reflected in the overall stack effect.

I can punt for now and only add register inst(...), so Irit can use the generator to define new register ops more easily, but this reduces the power of the story about combining micro-ops into macro-instructions, so I think eventually we will need an answer.

One possibility would be to use name correspondence, so that e.g.

register op(X, (arg1 -- temp1)) { ... }
register op(Y, (temp1 -- res1)) { ... }
macro(Z) = X + Y;

implies that Z has one register input (oparg1==arg1) and one register output (oparg2==res1) and one value that gets transferred between the micro-ops (temp1) -- in the current composition model the latter will just become a local variable that is never spilled to the register array. (In a world where we have a separate micro-op interpreter, we'd just have to spill it. This will be interesting because the bytecode->micro-code translator would have to allocate a register in this case.)

Note that in the register world we're probably not going to have super-instructions, so we won't have to think about those (but since this made me think of them, they would not have this problem because each component has its own three opargs).

Thoughts (@markshannon, @brandtbucher, @iritkatriel)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12Things we intend to do for 3.12epic-generatorDSL-based code generator for the interpreter(s)epic-registersIdeas related to a register VM

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions