repo_description: Intermediate Language (IL) for Hardware Accelerator Generators
repo_topics: compiler fpga-programming high-level-synthesis intermediate-language open-source-hardware
code: //! Command line parsing for the Calyx compiler.
  use argh::FromArgs;
  #[cfg(feature = "serialize")]
  use calyx_backend::SexpBackend;
  use calyx_backend::{
      Backend, BackendOpt, FirrtlBackend, MlirBackend, PrimitiveUsesBackend,
      ResourcesBackend, VerilogBackend,
      xilinx::{XilinxInterfaceBackend, XilinxXmlBackend},
  };
  use calyx_ir as ir;
  use calyx_utils::{CalyxResult, Error, OutputFile};
  use std::path::Path;
  use std::path::PathBuf;
  use std::str::FromStr;
  
  /// help information about passes
  #[derive(FromArgs, PartialEq, Debug)]
  #[argh(subcommand, name = "pass-help")]
  pub struct Help {
      /// alias or pass name to get help for
      #[argh(positional)]
      pub name: Option<String>,
  }
  
  /// formatter
  #[derive(FromArgs, PartialEq, Debug)]
  #[argh(subcommand, name = "format")]
  pub struct Format {
      /// input calyx file to format
      #[argh(positional)]
      pub file: PathBuf,
  }
  
  /// supported subcommands
  #[derive(FromArgs, PartialEq, Debug)]
  #[argh(subcommand)]
  pub enum Subcommand {
      /// Help mode
      Help(Help),
      /// Format
      Format(Format),
  }
  
  #[derive(FromArgs)]
  /// Options passed to the Calyx compiler.
  pub struct Opts {
      #[argh(subcommand)]
      pub sub: Option<Subcommand>,
  
      /// input calyx program
      #[argh(positional, from_str_fn(read_path))]
      pub file: Option<PathBuf>,
  
      /// output file
      #[argh(option, short = 'o', default = "OutputFile::Stdout")]
      pub output: OutputFile,
  
      /// path to the primitives library
      #[argh(
          option,
          short = 'l',
          default = "vec![option_env!(\"CALYX_PRIMITIVES_DIR\").unwrap_or(\".\").into()]"
      )]
      pub lib_path: Vec<PathBuf>,
  
      /// compilation mode
      #[argh(option, short = 'm', default = "CompileMode::default()")]
      pub compile_mode: CompileMode,
  
      /// enable synthesis mode
      #[argh(switch, long = "synthesis")]
      pub enable_synthesis: bool,
  
      /// disable verification checks emitted by backends
      #[argh(switch)]
      pub disable_verify: bool,
  
      /// emit nested assignments (only relevant to the Verilog backend)
      #[argh(switch, long = "nested")]
      pub nested_assign: bool,
  
      /// emit extmodules to use with SystemVerilog implementations
      /// of primitives (only relevant to the FIRRTL backend)
      #[argh(switch, long = "emit-primitive-extmodules")]
      pub emit_primitive_extmodules: bool,
  
      /// select a backend
      #[argh(option, short = 'b', default = "BackendOpt::default()")]
      pub backend: BackendOpt,
  
      /// run this pass during execution
      #[argh(option, short = 'p')]
      pub pass: Vec<String>,
  
      /// disable pass during execution
      #[argh(option, short = 'd', long = "disable-pass")]
      pub disable_pass: Vec<String>,
  
      /// extra options passed to the context
      #[argh(option, short = 'x', long = "extra-opt")]
      pub extra_opts: Vec<String>,
  
      /// establish a relative ordering of passes
      #[argh(option, short = 'i', long = "insert")]
      pub insertions: Vec<String>,
  
      /// enable verbose printing
      #[argh(option, long = "log", default = "log::LevelFilter::Warn")]
      pub log_level: log::LevelFilter,
  
      #[argh(switch, long = "dump-ir")]
      /// print out the IR after every pass
      pub dump_ir: bool,
  
      #[argh(switch, long = "version")]
      /// print out the version information
      pub version: bool,
  }
  
  fn read_path(path: &str) -> Result<PathBuf, String> {
      Ok(Path::new(path).into())
  }
  
  // Compilation modes
  #[derive(Default, PartialEq, Eq)]
  pub enum CompileMode {
      /// Compile the input file and ignore the dependencies.
      File,
      #[default]
      /// Transitively compile all dependencies `import`ed by the input file.
      Project,
  }
  
  impl FromStr for CompileMode {
      type Err = String;
  
      fn from_str(s: &str) -> Result<Self, Self::Err> {
          match s {
              "file" => Ok(CompileMode::File),
              "project" => Ok(CompileMode::Project),
              s => Err(format!(
                  "Unknown compilation mode: {s}. Valid options are `file` or `project`"
              )),
          }
      }
  }
  
  // ================== Backend Variant and Parsing ===================== //
  
  impl Opts {
      /// Given a context, calls the backend corresponding to the `BackendOpt` variant
      pub fn run_backend(mut self, context: ir::Context) -> CalyxResult<()> {
          match self.backend {
              BackendOpt::Mlir => {
                  let backend = MlirBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::Resources => {
                  let backend = ResourcesBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::Sexp => {
                  #[cfg(feature = "serialize")]
                  {
                      let backend = SexpBackend;
                      backend.run(context, self.output)
                  }
                  #[cfg(not(feature = "serialize"))]
                  {
                      Err(Error::misc(
                          "Sexp backend requires the `serialize` feature to be enabled",
                      ))
                  }
              }
              BackendOpt::Verilog => {
                  let backend = VerilogBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::Xilinx => {
                  let backend = XilinxInterfaceBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::XilinxXml => {
                  let backend = XilinxXmlBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::Firrtl => {
                  let backend = FirrtlBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::PrimitiveUses => {
                  let backend = PrimitiveUsesBackend;
                  backend.run(context, self.output)
              }
              BackendOpt::Calyx => {
                  ir::Printer::write_context(
                      &context,
                      false,
                      &mut self.output.get_write(),
                  )?;
                  Ok(())
              }
              BackendOpt::None => Ok(()),
          }
      }
  
      /// Get the current set of options from the command line invocation.
      pub fn get_opts() -> CalyxResult<Opts> {
          let mut opts: Opts = argh::from_env();
  
          if opts.compile_mode == CompileMode::File
              && !matches!(opts.backend, BackendOpt::Calyx | BackendOpt::None)
          {
              return Err(Error::misc(format!(
                  "--compile-mode=file is only valid with -b calyx. `-b {}` requires --compile-mode=project",
                  opts.backend
              )));
          }
  
          // argh doesn't allow us to specify a default for this so we fill it
          // in manually.
          if opts.pass.is_empty() {
              opts.pass = vec!["all".into()];
          }
  
          Ok(opts)
      }
  }
  