Skip to content

Commit

Permalink
modify compilation strategy to allow for io defs
Browse files Browse the repository at this point in the history
reorganizes the compilation source so that user-defined defs
and stdlib defs are all in the same map, so they can be treated
equally when compiling instructions.

empty defs are created and hoisted to the top of `insert_into_host`
to allow for mutually recursive definitions.
  • Loading branch information
enricozb committed Apr 19, 2024
1 parent f464b59 commit a20e4c1
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 30 deletions.
96 changes: 75 additions & 21 deletions src/compile.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#![cfg(feature = "std")]

use alloc::collections::BTreeSet;

use crate::prelude::*;

use crate::{
host::Host,
run::{Instruction, InterpretedDef, LabSet, Port, Tag},
run::{Def, Instruction, InterpretedDef, LabSet, Port, Tag},
stdlib::HostedDef,
};
use core::{fmt::Write, hash::Hasher};
Expand All @@ -25,39 +27,91 @@ fn _compile_host(host: &Host) -> Result<String, fmt::Error> {
.map(|(raw_name, def)| (raw_name, sanitize_name(raw_name), def));

writeln!(code, "#![allow(non_upper_case_globals, unused_imports)]")?;
writeln!(code, "use crate::{{host::{{Host, DefRef}}, run::*, ops::{{TypedOp, Ty::*, Op::*}}}};")?;
writeln!(
code,
"use crate::{{host::{{Host, DefRef}}, stdlib::{{AsHostedDef, HostedDef}}, run::*, ops::{{TypedOp, Ty::*, Op::*}}}};"
)?;
writeln!(code)?;

writeln!(code, "pub fn host() -> Host {{")?;
writeln!(code, " let mut host = Host::default();")?;
for (raw_name, name, _) in defs.clone() {
writeln!(code, r##" host.insert_def(r#"{raw_name}"#, DefRef::Static(unsafe {{ &*DEF_{name} }}));"##)?;
}
writeln!(code, " host")?;
writeln!(code, "}}\n")?;
writeln!(code, "pub fn insert_into_host(host: &mut Host) {{")?;

for (_, name, def) in defs.clone() {
// insert empty defs
for (hvmc_name, rust_name, def) in defs.clone() {
let labs = compile_lab_set(&def.labs)?;
writeln!(
code,
"pub const DEF_{name}: *const Def = const {{ &Def::new({labs}, (call_{name}, call_{name})) }}.upcast();"
r##" host.insert_def(r#"{hvmc_name}"#, unsafe {{ HostedDef::<Def_{rust_name}>::new({labs}) }});"##
)?;
}
writeln!(code)?;

// hoist all unique refs present in the right hand side of some def
for hvmc_name in defs.clone().flat_map(|(_, _, def)| refs(host, &def.data.0.instr)).collect::<BTreeSet<_>>() {
let rust_name = sanitize_name(hvmc_name);

writeln!(code, r##" let def_{rust_name} = Port::new_ref(&host.defs[r#"{hvmc_name}"#]);"##)?;
}
writeln!(code)?;

// initialize each def with the refs it makes use of
for (hvmc_name, rust_name, def) in defs.clone() {
let refs =
refs(host, &def.data.0.instr).iter().map(|r| format!("def_{}", sanitize_name(r))).collect::<Vec<_>>().join(", ");

writeln!(
code,
r##" host.get_mut::<HostedDef<Def_{rust_name}>>(r#"{hvmc_name}"#).data.0 = Def_{rust_name} {{ {refs} }};"##
)?;
}

writeln!(code, "}}")?;
writeln!(code)?;

for (_, name, def) in defs {
compile_def(&mut code, host, &name, &def.data.0.instr)?;
for (_, rust_name, def) in defs.clone() {
compile_struct(&mut code, host, &rust_name, def)?;
}

Ok(code)
}

fn compile_def(code: &mut String, host: &Host, name: &str, instr: &[Instruction]) -> fmt::Result {
writeln!(code, "pub fn call_{name}<M: Mode>(net: &mut Net<M>, to: Port) {{")?;
writeln!(code, " let t0 = Trg::port(to);")?;
for instr in instr {
write!(code, " ")?;
fn refs<'a>(host: &'a Host, instructions: &'a [Instruction]) -> BTreeSet<&'a str> {
let mut refs = BTreeSet::new();

for instr in instructions {
if let Instruction::Const { port, .. } | Instruction::LinkConst { port, .. } = instr {
if port.tag() == Tag::Ref {
refs.insert(host.back[&port.addr()].as_str());
}
}
}

refs
}

fn compile_struct(
code: &mut String,
host: &Host,
rust_name: &str,
def: &Def<HostedDef<InterpretedDef>>,
) -> fmt::Result {
let refs = refs(host, &def.data.0.instr)
.iter()
.map(|r| format!("def_{}: Port", sanitize_name(r)))
.collect::<Vec<_>>()
.join(",\n ");

writeln!(code, "#[derive(Default)]")?;
writeln!(code, "struct Def_{rust_name} {{")?;
writeln!(code, " {refs}")?;
writeln!(code, "}}")?;
writeln!(code)?;

writeln!(code, "impl AsHostedDef for Def_{rust_name} {{")?;
writeln!(code, " fn call<M: Mode>(slf: &Def<Self>, net: &mut Net<M>, port: Port) {{")?;
writeln!(code, " let t0 = Trg::port(port);")?;

for instr in &def.data.0.instr {
write!(code, " ")?;
match instr {
Instruction::Const { trg, port } => {
writeln!(code, "let {trg} = Trg::port({});", compile_port(host, port))
Expand Down Expand Up @@ -85,8 +139,8 @@ fn compile_def(code: &mut String, host: &Host, name: &str, instr: &[Instruction]
}
}?;
}
writeln!(code, " }}")?;
writeln!(code, "}}")?;
code.write_char('\n')?;

Ok(())
}
Expand All @@ -96,11 +150,11 @@ fn compile_port(host: &Host, port: &Port) -> String {
"Port::ERA".to_owned()
} else if port.tag() == Tag::Ref {
let name = sanitize_name(&host.back[&port.addr()]);
format!("Port::new_ref(unsafe {{ &*DEF_{name} }})")
format!("slf.data.def_{name}.clone()")
} else if port.tag() == Tag::Int {
format!("Port::new_int({})", port.int())
} else if port.tag() == Tag::F32 {
format!("Port::new_float({})", port.float())
format!("Port::new_float({:?})", port.float())
} else {
unreachable!()
}
Expand Down
4 changes: 1 addition & 3 deletions src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@

use crate::host::Host;

pub fn host() -> Host {
Host::default()
}
pub fn insert_into_host(_: &mut Host) {}
13 changes: 9 additions & 4 deletions src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ impl Host {
// each of the new defs.
for (nam, net) in book.iter() {
let data = self.encode_def(net);
match self.defs.get_mut(nam).unwrap() {
DefRef::Owned(def) => def.downcast_mut::<HostedDef<InterpretedDef>>().unwrap().data.0 = data,
DefRef::Static(_) => unreachable!(),
}
self.get_mut::<HostedDef<InterpretedDef>>(nam).data.0 = data;
}
}

Expand All @@ -103,4 +100,12 @@ impl Host {
self.back.insert(Port::new_ref(&def).addr(), name.to_owned());
self.defs.insert(name.to_owned(), def);
}

/// Returns a mutable [`Def`] named `name`.
pub fn get_mut<T: Send + Sync + 'static>(&mut self, name: &str) -> &mut Def<T> {
match self.defs.get_mut(name).unwrap() {
DefRef::Owned(def) => def.downcast_mut().unwrap(),
DefRef::Static(_) => unreachable!(),
}
}
}
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ fn main() {
}
} else {
let cli = BareCli::parse();
let host = hvmc::gen::host();
run(Arc::new(Mutex::new(host)), cli.opts, cli.args);
let host = create_host(&Book::default());
gen::insert_into_host(&mut host.lock());
run(host, cli.opts, cli.args);
}
if cfg!(feature = "trace") {
hvmc::trace::_read_traces(usize::MAX);
Expand Down
7 changes: 7 additions & 0 deletions src/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ impl<T: AsArcDef> AsDef for ArcDef<T> {
pub struct HostedDef<T: AsHostedDef>(pub T, PhantomData<()>);

impl<T: AsHostedDef> HostedDef<T> {
pub unsafe fn new(labs: LabSet) -> DefRef
where
T: Default,
{
Self::new_hosted(labs, T::default())
}

pub unsafe fn new_hosted(labs: LabSet, data: T) -> DefRef {
DefRef::Owned(Box::new(Def::new(labs, HostedDef(data, PhantomData))))
}
Expand Down

0 comments on commit a20e4c1

Please sign in to comment.