Add custom entrypoint option

This commit is contained in:
imaqtkatt 2024-02-08 15:32:04 -03:00
parent 05c31c55eb
commit 7a38b42645
9 changed files with 67 additions and 38 deletions

View File

@ -1,9 +1,7 @@
// Reduce the compiled networks, solving any annihilations and commutations.
// This is a useful optimization on its own, but also required by an hvm-core optimization.
use crate::{
expand, net_from_runtime, net_to_runtime, rdex, reduce, runtime_net_to_runtime_def, ENTRY_POINT,
};
use crate::{expand, net_from_runtime, net_to_runtime, rdex, reduce, runtime_net_to_runtime_def};
use hvmc::{
ast::{book_from_runtime, name_to_val, Book},
run::{Net, Ptr, OP2, REF},
@ -14,7 +12,7 @@ const MAX_ITERS: usize = 100_000;
/// Reduces the definitions in the book individually, except for main.
/// If cross_refs, will deref and try to find the smallest net.
/// Otherwise, just apply node~node interactions.
pub fn pre_reduce_book(book: &mut Book, cross_refs: bool) -> Result<(), String> {
pub fn pre_reduce_book(book: &mut Book, cross_refs: bool, entrypoint: String) -> Result<(), String> {
let rt_book = &mut hvmc::ast::book_to_runtime(book);
for (nam, net) in book.iter() {
// Skip unnecessary work
@ -26,7 +24,7 @@ pub fn pre_reduce_book(book: &mut Book, cross_refs: bool) -> Result<(), String>
boot(rt, fid);
expand(rt, rt_book);
let fully_reduce = cross_refs && nam != ENTRY_POINT;
let fully_reduce = cross_refs && *nam != entrypoint;
let iters = if fully_reduce {
let mut iters = 0;
// TODO: If I just call `rt.normal` some terms expand infinitely, so I put this workaround.

View File

@ -1,14 +1,13 @@
use crate::ENTRY_POINT;
use hvmc::{
ast::{val_to_name, Book, Tree},
run::Val,
};
use std::collections::HashSet;
pub fn prune_defs(book: &mut Book) {
pub fn prune_defs(book: &mut Book, entrypoint: String) {
let mut used_defs = HashSet::new();
// On hvmc, the entry point is always "main"
let mut to_visit = vec![ENTRY_POINT.to_string()];
// Start visiting the given entrypoint
let mut to_visit = vec![entrypoint.clone()];
while let Some(nam) = to_visit.pop() {
let def = &book[&nam];
@ -19,7 +18,7 @@ pub fn prune_defs(book: &mut Book) {
}
}
let used_defs = used_defs.into_iter().map(val_to_name).collect::<HashSet<_>>();
book.retain(|nam, _| used_defs.contains(nam) || nam == ENTRY_POINT);
book.retain(|nam, _| used_defs.contains(nam) || *nam == entrypoint);
}
fn used_defs_in_tree(tree: &Tree, used_defs: &mut HashSet<Val>, to_visit: &mut Vec<String>) {

View File

@ -13,7 +13,7 @@ use term::{
book_to_nets, net_to_term,
net_to_term::ReadbackErrors,
term_to_net::{HvmcNames, Labels},
Book, DefName, Term,
Book, DefName, Name, Term,
};
pub mod hvmc_net;
@ -27,26 +27,34 @@ pub const HVM1_ENTRY_POINT: &str = "Main";
pub fn check_book(mut book: Book) -> Result<(), String> {
// TODO: Do the checks without having to do full compilation
compile_book(&mut book, CompileOpts::light())?;
compile_book(&mut book, CompileOpts::light(), None)?;
Ok(())
}
pub fn compile_book(book: &mut Book, opts: CompileOpts) -> Result<CompileResult, String> {
let (main, warnings) = desugar_book(book, opts)?;
pub fn compile_book(
book: &mut Book,
opts: CompileOpts,
entrypoint: Option<Name>,
) -> Result<CompileResult, String> {
let (main, warnings) = desugar_book(book, opts, entrypoint)?;
let (nets, hvmc_names, labels) = book_to_nets(book, &main);
let mut core_book = nets_to_hvmc(nets, &hvmc_names)?;
if opts.pre_reduce {
pre_reduce_book(&mut core_book, opts.pre_reduce_refs)?;
pre_reduce_book(&mut core_book, opts.pre_reduce_refs, book.entrypoint())?;
}
if opts.prune {
prune_defs(&mut core_book);
prune_defs(&mut core_book, book.entrypoint());
}
Ok(CompileResult { core_book, hvmc_names, labels, warnings })
}
pub fn desugar_book(book: &mut Book, opts: CompileOpts) -> Result<(DefName, Vec<Warning>), String> {
pub fn desugar_book(
book: &mut Book,
opts: CompileOpts,
entrypoint: Option<Name>,
) -> Result<(DefName, Vec<Warning>), String> {
let mut warnings = Vec::new();
let main = book.check_has_main()?;
let main = book.check_has_entrypoint(entrypoint)?;
book.check_shared_names()?;
book.generate_scott_adts();
book.encode_builtins();
@ -104,8 +112,10 @@ pub fn run_book(
run_opts: RunOpts,
warning_opts: WarningOpts,
compile_opts: CompileOpts,
entrypoint: Option<Name>,
) -> Result<(Term, RunInfo), String> {
let CompileResult { core_book, hvmc_names, labels, warnings } = compile_book(&mut book, compile_opts)?;
let CompileResult { core_book, hvmc_names, labels, warnings } =
compile_book(&mut book, compile_opts, entrypoint)?;
display_warnings(warning_opts, &warnings)?;

View File

@ -1,8 +1,7 @@
use clap::{Args, CommandFactory, Parser, Subcommand};
use hvmc::ast::{show_book, show_net};
use hvml::{
check_book, compile_book, desugar_book, load_file_to_book, run_book, CompileOpts, RunInfo, RunOpts,
WarnState, WarningOpts,
check_book, compile_book, desugar_book, load_file_to_book, run_book, term::Name, CompileOpts, RunInfo, RunOpts, WarnState, WarningOpts
};
use std::{path::PathBuf, vec::IntoIter};
@ -14,6 +13,9 @@ struct Cli {
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short = 'e', long, global = true)]
pub entrypoint: Option<Name>,
}
#[derive(Subcommand, Clone, Debug)]
@ -168,14 +170,14 @@ fn execute_cli_mode(cli: Cli, verbose: &dyn Fn(&hvml::term::Book)) -> Result<(),
let mut book = load_file_to_book(&path)?;
verbose(&book);
let compiled = compile_book(&mut book, opts)?;
let compiled = compile_book(&mut book, opts, cli.entrypoint)?;
hvml::display_warnings(warning_opts, &compiled.warnings)?;
print!("{}", show_book(&compiled.core_book));
}
Mode::Desugar { path } => {
let mut book = load_file_to_book(&path)?;
verbose(&book);
desugar_book(&mut book, CompileOpts::default())?;
desugar_book(&mut book, CompileOpts::default(), None)?;
println!("{book}");
}
Mode::Run { path, mem, debug, mut single_core, linear, arg_stats, cli_opts, wopts, lazy_mode } => {
@ -198,7 +200,7 @@ fn execute_cli_mode(cli: Cli, verbose: &dyn Fn(&hvml::term::Book)) -> Result<(),
let mem_size = mem / std::mem::size_of::<(hvmc::run::APtr, hvmc::run::APtr)>();
let run_opts = RunOpts { single_core, debug, linear, lazy_mode };
let (res_term, RunInfo { stats, readback_errors, net }) =
run_book(book, mem_size, run_opts, warning_opts, opts)?;
run_book(book, mem_size, run_opts, warning_opts, opts, cli.entrypoint)?;
let total_rewrites = stats.rewrites.total() as f64;
let rps = total_rewrites / stats.run_time / 1_000_000.0;

View File

@ -1,10 +1,20 @@
use crate::{
term::{Book, DefName},
term::{Book, DefName, Name},
ENTRY_POINT, HVM1_ENTRY_POINT,
};
impl Book {
pub fn check_has_main(&self) -> Result<DefName, String> {
pub fn check_has_entrypoint(&mut self, nam: Option<Name>) -> Result<DefName, String> {
if let Some(nam) = nam {
match self.defs.get(&nam) {
Some(_) => {
self.main = Some(nam.clone());
return Ok(nam);
}
None => return Err(format!("File has no '{nam}' definition")),
}
}
match (self.defs.get(&DefName::new(ENTRY_POINT)), self.defs.get(&DefName::new(HVM1_ENTRY_POINT))) {
(None, None) => Err("File has no 'main' definition".to_string()),
(Some(_), Some(_)) => Err("File has both 'Main' and 'main' definitions".to_string()),
@ -14,6 +24,7 @@ impl Book {
} else if !main.rules[0].pats.is_empty() {
Err("Main definition can't have any arguments".to_string())
} else {
self.main = None;
Ok(main.name.clone())
}
}

View File

@ -1,6 +1,6 @@
pub mod ctrs_arities;
pub mod exhaustiveness;
pub mod has_main;
pub mod has_entrypoint;
pub mod shared_names;
pub mod type_check;
pub mod unbound_pats;

View File

@ -16,7 +16,7 @@ pub mod transform;
pub use net_to_term::{net_to_term, ReadbackError};
pub use term_to_net::{book_to_nets, term_to_compat_net};
use crate::term::builtins::*;
use crate::{term::builtins::*, ENTRY_POINT};
/// The representation of a program.
#[derive(Debug, Clone, Default)]
@ -29,6 +29,9 @@ pub struct Book {
/// To which type does each constructor belong to.
pub ctrs: IndexMap<DefName, DefName>,
/// A custom or default entrypoint.
pub main: Option<Name>,
}
/// A pattern matching function definition.
@ -786,3 +789,9 @@ impl Deref for Name {
self.0.deref()
}
}
impl Book {
pub fn entrypoint(&self) -> String {
if let Some(nam) = &self.main { nam.to_string() } else { ENTRY_POINT.to_string() }
}
}

View File

@ -25,7 +25,7 @@ pub fn book_to_nets(book: &Book, main: &DefName) -> (HashMap<String, INet>, Hvmc
for rule in def.rules.iter() {
let net = term_to_compat_net(&rule.body, &mut labels);
let name = if def.name == *main {
let name = if def.name == *main && book.main.is_none() {
ENTRY_POINT.to_string()
} else {
def_name_to_hvmc_name(&def.name, &nets, &mut generated_count)

View File

@ -96,7 +96,7 @@ fn compile_term() {
fn compile_file_o_all() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let compiled = compile_book(&mut book, CompileOpts::heavy())?;
let compiled = compile_book(&mut book, CompileOpts::heavy(), None)?;
Ok(format!("{:?}", compiled))
})
}
@ -104,7 +104,7 @@ fn compile_file_o_all() {
fn compile_file() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let compiled = compile_book(&mut book, CompileOpts::light())?;
let compiled = compile_book(&mut book, CompileOpts::light(), None)?;
Ok(format!("{:?}", compiled))
})
}
@ -115,7 +115,7 @@ fn run_file() {
let book = do_parse_book(code, path)?;
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) =
run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy())?;
run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy(), None)?;
Ok(format!("{}{}", info.readback_errors.display(), res.display()))
})
}
@ -130,7 +130,7 @@ fn run_lazy() {
desugar_opts.lazy_mode();
// 1 million nodes for the test runtime. Smaller doesn't seem to make it any faster
let (res, info) = run_book(book, 1 << 20, run_opts, WarningOpts::deny_all(), desugar_opts)?;
let (res, info) = run_book(book, 1 << 20, run_opts, WarningOpts::deny_all(), desugar_opts, None)?;
Ok(format!("{}{}", info.readback_errors.display(), res.display()))
})
}
@ -150,7 +150,7 @@ fn readback_lnet() {
fn flatten_rules() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let main = book.check_has_main().ok();
let main = book.check_has_entrypoint(None).ok();
book.check_shared_names()?;
book.encode_builtins();
book.resolve_ctrs_in_pats();
@ -177,7 +177,7 @@ fn parse_file() {
fn encode_pattern_match() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
let main = book.check_has_main().ok();
let main = book.check_has_entrypoint(None).ok();
book.check_shared_names()?;
book.generate_scott_adts();
book.encode_builtins();
@ -192,7 +192,7 @@ fn encode_pattern_match() {
fn desugar_file() {
run_golden_test_dir(function_name!(), &|code, path| {
let mut book = do_parse_book(code, path)?;
desugar_book(&mut book, CompileOpts::light())?;
desugar_book(&mut book, CompileOpts::light(), None)?;
Ok(book.to_string())
})
}
@ -208,7 +208,8 @@ fn hangs() {
let lck = Arc::new(RwLock::new(false));
let got = lck.clone();
std::thread::spawn(move || {
let _ = run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy());
let _ =
run_book(book, 1 << 20, RunOpts::default(), WarningOpts::deny_all(), CompileOpts::heavy(), None);
*got.write().unwrap() = true;
});
std::thread::sleep(std::time::Duration::from_secs(expected_normalization_time));
@ -216,4 +217,3 @@ fn hangs() {
if !*lck.read().unwrap() { Ok("Hangs".into()) } else { Err("Doesn't hang".into()) }
})
}