HigherOrderCO/hvm-64

support IO in compiled binaries

Closed this issue · 0 comments

Currently, compiled binaries are generated in a way that assumes all definitions are knowable at compile-time, but this is not the case for IO defs.

Right now the compiled output looks roughly like:

@foo = *
@bar = @foo
// gen.rs

pub fn host() -> Host {
  let mut host = Host::default();
  host.insert_def(r#"foo"#, DefRef::Static(unsafe { &*DEF_foo }));
  host.insert_def(r#"bar"#, DefRef::Static(unsafe { &*DEF_bar }));
  host
}

pub const DEF_foo: *const Def = const { &Def::new(LabSet::NONE, (call_foo, call_foo)) }.upcast();
pub const DEF_bar: *const Def = const { &Def::new(LabSet::NONE, (call_bar, call_bar)) }.upcast();

pub fn call_foo<M: Mode>(net: &mut Net<M>, to: Port) {
  let t0 = Trg::port(to);
  net.link_trg(t0, Trg::port(Port::ERA));
}

pub fn call_bar<M: Mode>(net: &mut Net<M>, to: Port) {
  let t0 = Trg::port(to);
  net.link_trg(t0, Trg::port(Port::new_ref(unsafe { &*DEF_foo })));
}

Instead, it should look like:

// gen.rs

pub fn insert_into_host(host: &mut Host) {
  host.insert_def(r#"foo"#, unsafe { HostedDef::new_hosted(LabSet::NONE, Def_foo::default()) });
  host.insert_def(r#"bar"#, unsafe { HostedDef::new_hosted(LabSet::NONE, Def_bar::default()) });

  let def_foo = Port::new_ref(&host.defs[r#"foo"#]);

  host.get_mut::<HostedDef<Def_bar>>(r#"bar"#).data.0 = Def_bar { def_foo };
}

#[derive(Default)]
struct Def_foo {}

impl AsHostedDef for Def_foo {
  fn call<M: Mode>(slf: &Def<Self>, net: &mut Net<M>, port: Port) {
    let t0 = Trg::port(port);
    net.link_trg(t0, Trg::port(Port::ERA));
  }
}

#[derive(Default)]
struct Def_bar {
  def_foo: Port,
}

impl AsHostedDef for Def_bar {
  fn call<M: Mode>(slf: &Def<Self>, net: &mut Net<M>, port: Port) {
    let t0 = Trg::port(port);
    net.link_trg(t0, Trg::port(slf.data.def_foo.clone()));
  }
}

(using this Host::get_mut api)

The insert_into_host function is somewhat peculiar because it needs to be able to handle circular references; thus, first, all of the definitions are inserted with meaningless default state, and then are all initialized with their dependencies.

This naturally supports using pre-defined IO defs with minimal modifications.

Then, in the compiled binary, one would simply write:

let mut host = create_host(&Book::default());
gen::insert_into_host(&mut host);

This would, of course, only support the IO operations hvm-core supports; I'll be opening another issue shortly for how that could be addressed.