ir+eval: Rework of instruction architecture #8

Open
opened 2024-01-21 18:26:44 +00:00 by multisamplednight · 0 comments
Collaborator

Status quo

#6 implemented a rather hacky match on providing the inputs and processing the outputs of each instruction, and namely doesn't even handle instructions with multiple outputs at the moment.

Goal

An architecture for instructions that seperates the code of the individual instructions, and allows the instructions in ir::instruction to list their inputs and outputs coherently and type-safely (such that we can make the inner values of the IDs in ir::id private/pub(super)).

Suggestion

ir

Not sure at all. Maybe we just need a bit of macro magic on ir::instruction::Kind which allows specifying inputs and outputs as well as some helpful docs. Possible example, which would require moving Variant out of eval (which is likely necessary for a type system anyway):

use Variant::*;

instrs! {
    /// Always returns the same value. Perfect for hard-coded values.
    Const {
        state: [Any],
        out: [Any],
    },
    /// Reads an image from the given path.
    Read {
        in: [Path],
        out: [Image],
    },
    /// Writes an image to the given path.
    Write {
        in: [Image, Path],
    },
    // filters
    /// In
    Invert {
        in: [Image],
        out: [Image],
    }
}

which would then expand into an enum Kind holding all those variants, and form additional methods returning

  1. how many input and output sockets there are
  2. what their return types are
eval

Possibly a trait Instruction in eval::debug would be beneficial, with a signature of somewhat like this:

trait Instruction {
    fn eval(&self, inputs: Vec<Variant>) -> Vec<Variant>;
}

Then there'd need to be some kind of glue to make writing instructions actually easy, since Variants themselves are quite tedious to extract. Maybe something like this (where the instr macro automatically extracts the individual types from the Variant and returns an error/panics if they do not match what's expected:

use ir::{instruction::Kind, value::Variant};

struct Invert

#[instr(Kind::Invert)]
impl Instruction for Invert {
    fn eval(mut image: DynamicImage) -> DynamicImage {
        image.invert();
        image
    }
}

Those could then be mapped somewhere in the debug evaluator:

fn instr_to_impl(kind: instruction::Kind) -> &'static dyn Instruction {
    todo!("idk some phf map magic?");
}

Other things

  • After this, we'd have quite a lot of things all named instruction or similar:
    • ir::instruction::Kind, a generic overview of what instructions they are and what types they accept and return
    • eval::kind::debug::Instruction, a specific implementation of a specific instruction
    • eval::kind::debug::instr, which helps implementing eval::kind::debug::Instruction by mapping the types appropiately
  • We should maybe make eval::kind::debug its own crate in order to allow parallel compilation to other evaluators, and make the module paths less complicated when referring to the debug evaluator and its children modules.
#### Status quo #6 implemented a rather hacky match on providing the inputs and processing the outputs of each instruction, and namely doesn't even handle instructions with multiple outputs at the moment. #### Goal An architecture for instructions that seperates the code of the individual instructions, and allows the instructions in `ir::instruction` to list their inputs and outputs coherently and type-safely (such that we can make the inner values of the IDs in `ir::id` private/`pub(super)`). #### Suggestion ##### `ir` Not sure at all. Maybe we just need a bit of macro magic on `ir::instruction::Kind` which allows specifying inputs and outputs as well as some helpful docs. Possible example, which would require moving `Variant` out of `eval` (which is likely necessary for a type system anyway): ```rs use Variant::*; instrs! { /// Always returns the same value. Perfect for hard-coded values. Const { state: [Any], out: [Any], }, /// Reads an image from the given path. Read { in: [Path], out: [Image], }, /// Writes an image to the given path. Write { in: [Image, Path], }, // filters /// In Invert { in: [Image], out: [Image], } } ``` which would then expand into an enum `Kind` holding all those variants, and form additional methods returning 1. how many input and output sockets there are 2. what their return types are ##### `eval` Possibly a trait `Instruction` in `eval::debug` would be beneficial, with a signature of somewhat like this: ```rs trait Instruction { fn eval(&self, inputs: Vec<Variant>) -> Vec<Variant>; } ``` Then there'd need to be some kind of glue to make writing instructions actually easy, since `Variant`s themselves are quite tedious to extract. Maybe something like this (where the `instr` macro automatically extracts the individual types from the `Variant` and returns an error/panics if they do not match what's expected: ```rs use ir::{instruction::Kind, value::Variant}; struct Invert #[instr(Kind::Invert)] impl Instruction for Invert { fn eval(mut image: DynamicImage) -> DynamicImage { image.invert(); image } } ``` Those could then be mapped somewhere in the debug evaluator: ```rs fn instr_to_impl(kind: instruction::Kind) -> &'static dyn Instruction { todo!("idk some phf map magic?"); } ``` #### Other things - After this, we'd have quite a lot of things all named instruction or similar: - `ir::instruction::Kind`, a generic overview of what instructions they are and what types they accept and return - `eval::kind::debug::Instruction`, a specific implementation of a specific instruction - `eval::kind::debug::instr`, which helps implementing `eval::kind::debug::Instruction` by mapping the types appropiately - We should maybe make `eval::kind::debug` its own crate in order to allow parallel compilation to other evaluators, and make the module paths less complicated when referring to the debug evaluator and its children modules.
Sign in to join this conversation.
No labels
proposal
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: katzen-cafe/iowo#8
No description provided.