Visitar URL original
Register instructions and ops in the instruction definition DSL · Issue #521 · faster-cpython/ideas · GitHub
Skip to content

Register instructions and ops in the instruction definition DSL #521

Closed

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)?

Activity

  1. gvanrossum commented on Dec 17, 2022

    @gvanrossum
    CollaboratorAuthor

    FWIW, Mark anticipated this when he wrote:

    Stack-based instructions are much more easily composed than register-based ones.

  2. iritkatriel commented on Dec 17, 2022

    @iritkatriel
    Collaborator

    We probably need to see use cases for macro(). I don’t know if it makes sense to have any where the second instruction doesn’t consume the output of the first.

  3. gvanrossum commented on Dec 18, 2022

    @gvanrossum
    CollaboratorAuthor

    There are some use cases for macro in the Examples section of the Instruction Definitions doc. There's this macro:

    macro ( LOAD_ATTR_INSTANCE_VALUE ) =
            counter/1 + CHECK_OBJECT_TYPE + CHECK_HAS_INSTANCE_VALUES +
            LOAD_INSTANCE_VALUE + unused/4 ;
    

    where the first two components "pass through" owner, which finally is consumed by LOAD_INSTANCE_VALUE:

        op ( CHECK_OBJECT_TYPE, (owner, type_version/2 -- owner) ) { ... }
        op ( CHECK_HAS_INSTANCE_VALUES, (owner -- owner) ) { ... }
        op ( LOAD_INSTANCE_VALUE, (owner, index/1 -- null if (oparg & 1), res) ) { ... }
    

    I guess the generator should pay attention to name correspondence between input and output effects of an op as well as between the output effect of one op and the input of the next. In the stack world the convention is that if an input has the same name as an output (and they refer to the same stack entry) the instruction is not supposed to write to the output, we could keep that convention. But we still need to add the convention that name correspondence between different components means "use the same oparg/register".

  4. added
    3.12Things we intend to do for 3.12
    on Feb 28, 2023
  5. iritkatriel commented on Mar 15, 2023

    @iritkatriel
    Collaborator

    Should we close this? Should we remove register instructions from the generator for 3.12?

  6. gvanrossum commented on Mar 15, 2023

    @gvanrossum
    CollaboratorAuthor

    I just ignore them for new features, so they probably don't work any more. Feel free to remove what's left of them, we will be able to use the git history to see what was there.

  7. iritkatriel commented on Mar 15, 2023

    @iritkatriel
    Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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