Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gen config #439

Merged
merged 12 commits into from
Apr 25, 2024
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ version = "0.1.0"
rust-version = "1.67"

[workspace.dependencies]
toml = "0.8"
serde = "1.0"
argh = "0.1"
log = "0.4"
pest = "2.1"
Expand Down
12 changes: 10 additions & 2 deletions crates/ast/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::path::PathBuf;

use super::{Command, Id, Signature};
use fil_gen as gen;
use gen::GenConfig;

#[derive(Default)]
/// A external or generate definition in Filament
Expand Down Expand Up @@ -53,6 +54,8 @@ pub struct Namespace {
pub components: Vec<Component>,
/// Top-level component id
pub toplevel: String,
/// Top level bindings
pub bindings: Vec<u64>,
}

impl Namespace {
Expand All @@ -61,6 +64,7 @@ impl Namespace {
imports: Vec::default(),
externs: Vec::default(),
components: Vec::default(),
bindings: Vec::default(),
toplevel,
}
}
Expand All @@ -74,8 +78,12 @@ impl Namespace {
/// REQUIRES: The tools definitions must be in files with absolute paths.
/// The folder containing the generated files is deleted when the destructor
/// for GenExec runs.
pub fn init_gen(&self, out_dir: Option<PathBuf>) -> gen::GenExec {
let mut gen_exec = gen::GenExec::new(false, out_dir);
pub fn init_gen(
&self,
out_dir: Option<PathBuf>,
config: GenConfig,
) -> gen::GenExec {
let mut gen_exec = gen::GenExec::new(false, out_dir, config);
for Extern { path, gen, .. } in &self.externs {
let Some(tool_name) = gen else {
continue;
Expand Down
2 changes: 2 additions & 0 deletions crates/filament/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ readme.workspace = true
rust-version.workspace = true

[dependencies]
toml.workspace = true
serde.workspace = true
argh.workspace = true
log.workspace = true
pest.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions crates/filament/src/cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ pub struct Opts {
#[argh(option, long = "out-dir")]
pub out_dir: Option<PathBuf>,

/// provided bindings (gen config and parameter bindings)
#[argh(option, long = "bindings")]
pub bindings: Option<PathBuf>,

// Backend options
/// backend to use (default: verilog): calyx, verilog
#[argh(option, long = "backend", default = "Backend::Verilog")]
Expand Down
2 changes: 2 additions & 0 deletions crates/filament/src/ir_passes/dump_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ impl DumpInterface {
pub fn print(ctx: &ir::Context) {
let entrypoint = ctx
.entrypoint
.as_ref()
.map(|ep| ep.comp)
.unwrap_or_else(|| panic!("No entrypoint found."));
let main = ctx.get(entrypoint);
let src_info = main
Expand Down
2 changes: 1 addition & 1 deletion crates/filament/src/ir_passes/lower/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl Compile {
component.attributes.insert(calyx::BoolAttr::NoInterface, 1);

// If this is the main component, give it a `@top_level` attribute
if Some(idx) == ctx.entrypoint {
if ctx.is_main(idx) {
log::debug!("Defining main component {idx}");
component.attributes.insert(calyx::BoolAttr::TopLevel, 1);
}
Expand Down
14 changes: 10 additions & 4 deletions crates/filament/src/ir_passes/mono/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
};
use fil_gen as gen;
use fil_ir::{self as ir, Ctx, IndexStore};
use ir::AddCtx;
use ir::{AddCtx, EntryPoint};
use itertools::Itertools;
use std::collections::HashMap;

Expand Down Expand Up @@ -277,22 +277,28 @@ impl Monomorphize<'_> {
ctx: &ir::Context,
gen: &mut Option<gen::GenExec>,
) -> ir::Context {
let Some(entrypoint) = ctx.entrypoint else {
let Some(entrypoint) = &ctx.entrypoint else {
log::warn!("Program has no entrypoint. Result will be empty.");
return ir::Context {
comps: IndexStore::default(),
entrypoint: None,
externals: HashMap::new(),
};
};
let EntryPoint {
comp: entrypoint,
bindings,
} = entrypoint;

let entrypoint = entrypoint.ul();
// Monomorphize the entrypoint
let mut mono = Monomorphize::new(ctx, gen);
let ck = CompKey::new(entrypoint, vec![]);
let ck = CompKey::new(entrypoint, bindings.clone());
mono.monomorphize(ck.clone());

let new_entrypoint = mono.processed.get(&ck).unwrap();
mono.ctx.entrypoint = Some(new_entrypoint.get());
// New component no longer has any bindings
mono.ctx.entrypoint = Some(EntryPoint::new(new_entrypoint.get()));
mono.ctx.externals = mono.ext_map;
ir::Validate::context(&mono.ctx);
mono.ctx
Expand Down
24 changes: 23 additions & 1 deletion crates/filament/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
use calyx_backend::Backend;
use calyx_opt::pass_manager::PassManager;
use fil_gen::GenConfig;
use fil_ir as ir;
use filament::ir_passes::BuildDomination;
use filament::{cmdline, ir_passes as ip, resolver::Resolver};
use filament::{log_pass, log_time, pass_pipeline};
use serde::Deserialize;
use std::fs;

#[derive(Deserialize, Default)]
#[serde(default)]
/// Contains the bindings that are provided by the user.
pub struct ProvidedBindings {
/// Gen configuration variables
gen: GenConfig,
/// Parameters to give to the top-level component
params: Vec<u64>,
}

// Prints out the interface for main component in the input program.
fn run(opts: &cmdline::Opts) -> Result<(), u64> {
Expand All @@ -16,16 +29,25 @@ fn run(opts: &cmdline::Opts) -> Result<(), u64> {
.target(env_logger::Target::Stderr)
.init();

// Load the provided bindings
let provided_bindings: ProvidedBindings = opts
.bindings
.as_ref()
.map(|path| toml::from_str(&fs::read_to_string(path).unwrap()).unwrap())
.unwrap_or_default();

let ns = match Resolver::from(opts).parse_namespace() {
Ok(mut ns) => {
ns.toplevel = opts.toplevel.clone();
ns.bindings = provided_bindings.params;
ns
}
Err(e) => {
eprintln!("Error: {e:?}");
return Err(1);
}
};

// Initialize the generator
let mut gen_exec = if ns.requires_gen() {
if opts.out_dir.is_none()
Expand All @@ -37,7 +59,7 @@ fn run(opts: &cmdline::Opts) -> Result<(), u64> {
"`--out-dir <dir>` to store the generated files."
))
}
Some(ns.init_gen(opts.out_dir.clone()))
Some(ns.init_gen(opts.out_dir.clone(), provided_bindings.gen))
} else {
None
};
Expand Down
4 changes: 2 additions & 2 deletions crates/gen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ version.workspace = true
rust-version.workspace = true

[dependencies]
serde = "1.0"
toml = "0.8"
toml.workspace = true
serde.workspace = true
argh.workspace = true
itertools.workspace = true
env_logger.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions crates/gen/src/config_schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Defines the schema for a config configuration file
use std::collections::HashMap;

/// A tool that can generate external modules for Filament
pub type GenConfig = HashMap<String, HashMap<String, String>>;
16 changes: 14 additions & 2 deletions crates/gen/src/exec.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Instance, Tool, ToolOutput};
use crate::{GenConfig, Instance, Tool, ToolOutput};
use itertools::Itertools;
use std::{
collections::HashMap,
Expand Down Expand Up @@ -64,17 +64,25 @@ pub struct GenExec {
/// Directory to store all the generated files
output_dir: OutDir,

/// Config file
config: GenConfig,

/// Dry-run instead of executing commands
dry_run: bool,
}

impl GenExec {
pub fn new(dry_run: bool, out_dir: Option<PathBuf>) -> Self {
pub fn new(
dry_run: bool,
out_dir: Option<PathBuf>,
config: GenConfig,
) -> Self {
GenExec {
tools: HashMap::default(),
generated: HashMap::default(),
output_dir: OutDir::opt(out_dir),
dry_run,
config,
}
}

Expand All @@ -94,6 +102,10 @@ impl GenExec {

let desc = fs::read_to_string(path.clone()).unwrap();
let mut tool: Tool = toml::from_str(&desc).unwrap();
// Replace the globals with the ones from the config file if it exists
if let Some(globals) = self.config.remove(&tool.name) {
tool.globals = globals;
}
// Get the absolute path to the binary if it is relative
let tool_path = PathBuf::from(&tool.path);
if !tool_path.is_absolute() {
Expand Down
2 changes: 2 additions & 0 deletions crates/gen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod cmdline;
mod config_schema;
mod exec;
mod tool_schema;

pub use cmdline::Opts;
pub use config_schema::GenConfig;
pub use exec::GenExec;
pub use tool_schema::{Instance, Manifest, Module, Tool, ToolOutput};
4 changes: 2 additions & 2 deletions crates/gen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fil_gen::GenExec;
use fil_gen::{GenConfig, GenExec};
use std::fs;

fn main() {
Expand All @@ -11,7 +11,7 @@ fn main() {
.filter_level(opts.log_level)
.target(env_logger::Target::Stderr)
.init();
let mut gen = GenExec::new(opts.dry_run, None);
let mut gen = GenExec::new(opts.dry_run, None, GenConfig::default());

// Deserialize the tool description
let desc = fs::read_to_string(opts.tool).unwrap();
Expand Down
21 changes: 18 additions & 3 deletions crates/ir/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@ use super::{
use fil_derive::Ctx;
use std::collections::HashMap;

/// Contains information for the entrypoint component.
pub struct EntryPoint {
pub comp: CompIdx,
pub bindings: Vec<u64>,
}

impl EntryPoint {
pub fn new(comp: CompIdx) -> Self {
Self {
comp,
bindings: Vec::new(),
}
}
}

#[derive(Default, Ctx)]
pub struct Context {
#[ctx(Component: Get, Add, Mut)]
pub comps: IndexStore<Component>,
// Contains external components grouped by file name.
/// Contains external components grouped by file name.
pub externals: HashMap<String, Vec<CompIdx>>,
pub entrypoint: Option<CompIdx>,
pub entrypoint: Option<EntryPoint>,
}

impl Context {
pub fn is_main(&self, idx: CompIdx) -> bool {
Some(idx) == self.entrypoint
Some(idx) == self.entrypoint.as_ref().map(|ep| ep.comp)
}

/// Is this component external?
Expand Down
55 changes: 52 additions & 3 deletions crates/ir/src/from_ast/astconv.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Convert the frontend AST to the IR.
use super::build_ctx::{OwnedParam, OwnedPort};
use super::{BuildCtx, Sig, SigMap};
use crate as ir;
use crate::utils::Idx;
use crate::{self as ir, EntryPoint};
use crate::{
AddCtx, Cmp, Ctx, DisplayCtx, EventIdx, ExprIdx, InterfaceSrc, MutCtx,
ParamIdx, PortIdx, PropIdx, TimeIdx,
Expand Down Expand Up @@ -1074,10 +1074,15 @@ fn try_transform(ns: ast::Namespace) -> BuildRes<ir::Context> {
entrypoint: ns
.main_idx()
// index main component after all externals
.map(|idx| Idx::new(ns.externals().count() + idx)),
.map(|idx| Idx::new(ns.externals().count() + idx))
.map(EntryPoint::new),
..Default::default()
};

// Loc<Id> of the toplevel component
let toplevel_id =
ns.main_idx().map(|pos| ns.components[pos].sig.name.clone());

// Walk over signatures and compile signatures to build a SigMap
// Contains a tuple containing three necessary bits of information:
// 1. The (optional) name of the component (if it is an external)
Expand Down Expand Up @@ -1121,7 +1126,7 @@ fn try_transform(ns: ast::Namespace) -> BuildRes<ir::Context> {
let mut builder = BuildCtx::new(ir::Component::new(typ), &sig_map);

// enable source information saving if this is main
if Some(idx) == ctx.entrypoint {
if ctx.is_main(idx) {
builder.comp().src_info =
Some(InterfaceSrc::new(sig.name.copy(), None))
}
Expand All @@ -1139,6 +1144,50 @@ fn try_transform(ns: ast::Namespace) -> BuildRes<ir::Context> {
// compile the signature
let irsig = builder.sig(idx, &sig)?;

// Now that all the signature has been compiled,
// if this component is the main component, add the provided
// bindings to the entrypoint
// this needs to be done here because we need to provide
// possible default values for the parameters
if ctx.is_main(idx) {
let Some(ep) = &mut ctx.entrypoint else {
unreachable!(
"Entrypoint not set despite main component existing"
);
};

let Some(toplevel_id) = &toplevel_id else {
unreachable!("Main component had no name")
};

ep.bindings = irsig
.param_binding(
ns.bindings.iter().copied().map(ast::Expr::Concrete),
toplevel_id.clone(),
builder.diag(),
)?
.into_iter()
.enumerate()
.map(|(i, (_, e))| {
if let ast::Expr::Concrete(e) = e {
Ok(e)
} else {
let diag = builder.diag();
let err = Error::malformed(
"Default values for parameters in the main component must be concrete",
).add_note(
diag.add_info(
"Parameter was not given a concrete value",
irsig.raw_params[i].pos()
)
);
diag.add_error(err);
Err(std::mem::take(diag))
}
})
.collect::<BuildRes<Vec<_>>>()?;
}

Ok((Builder { idx, builder, body }, (sig.name.take(), irsig)))
})
.collect::<BuildRes<Vec<_>>>()?
Expand Down
Loading
Loading