xlsynth-driver 0.61.0

Binary that integrates XLS capabilities into a driver program
Documentation
// SPDX-License-Identifier: Apache-2.0

use clap::ArgMatches;
use xlsynth_g8r::aig::cut_db_rewrite::CutDbRewriteMode;
use xlsynth_g8r::gate_fn_optimize::GateFnOptimizeOptions;
use xlsynth_g8r::ir2gate_utils::AdderMapping;
use xlsynth_g8r::process_ir_path;
use xlsynth_g8r::process_ir_path::{CanonicalG8rOptions, DEFAULT_MAX_FRAIG_SIM_SAMPLES};
use xlsynth_g8r::prove_gate_fn_equiv_common::GateFormalBackend;
use xlsynth_g8r::prove_gate_fn_equiv_sat::DEFAULT_CADICAL_TERMINATE_LIMIT;

fn parse_adder_mapping(value: Option<&str>) -> AdderMapping {
    match value {
        Some("ripple-carry") => AdderMapping::RippleCarry,
        Some("brent-kung") => AdderMapping::BrentKung,
        Some("kogge-stone") => AdderMapping::KoggeStone,
        _ => AdderMapping::default(),
    }
}

fn parse_adder_mappings(matches: &ArgMatches) -> (AdderMapping, Option<AdderMapping>) {
    let adder_mapping = parse_adder_mapping(
        matches
            .get_one::<String>("adder_mapping")
            .map(|s| s.as_str()),
    );
    let mul_adder_mapping = match matches
        .get_one::<String>("mul_adder_mapping")
        .map(|s| s.as_str())
    {
        Some("ripple-carry") => Some(AdderMapping::RippleCarry),
        Some("brent-kung") => Some(AdderMapping::BrentKung),
        Some("kogge-stone") => Some(AdderMapping::KoggeStone),
        _ => None,
    };
    (adder_mapping, mul_adder_mapping)
}

fn parse_bool(matches: &ArgMatches, name: &str, default: bool) -> bool {
    match matches.get_one::<String>(name).map(|s| s.as_str()) {
        Some("true") => true,
        Some("false") => false,
        _ => default,
    }
}

fn parse_f64_default(matches: &ArgMatches, name: &str, default: f64) -> f64 {
    matches
        .get_one::<String>(name)
        .and_then(|s| s.parse::<f64>().ok())
        .unwrap_or(default)
}

fn parse_usize_default(matches: &ArgMatches, name: &str, default: usize) -> usize {
    matches
        .get_one::<String>(name)
        .and_then(|s| s.parse::<usize>().ok())
        .unwrap_or(default)
}

fn parse_usize_or_exit(
    matches: &ArgMatches,
    name: &str,
    flag_name_for_error: &str,
    default: usize,
) -> usize {
    let Some(value) = matches.get_one::<String>(name) else {
        return default;
    };
    match value.parse::<usize>() {
        Ok(n) => n,
        Err(_) => {
            eprintln!("Invalid {flag_name_for_error}: {value:?}");
            std::process::exit(1);
        }
    }
}

fn parse_u64_default(matches: &ArgMatches, name: &str, default: u64) -> u64 {
    matches
        .get_one::<String>(name)
        .and_then(|s| s.parse::<u64>().ok())
        .unwrap_or(default)
}

fn parse_u32_or_exit(
    matches: &ArgMatches,
    name: &str,
    flag_name_for_error: &str,
    default: u32,
) -> u32 {
    let Some(value) = matches.get_one::<String>(name) else {
        return default;
    };
    match value.parse::<u32>() {
        Ok(n) => n,
        Err(_) => {
            eprintln!("Invalid {flag_name_for_error}: {value:?}");
            std::process::exit(1);
        }
    }
}

fn parse_gate_formal_backend_or_exit(matches: &ArgMatches) -> GateFormalBackend {
    let value = matches
        .get_one::<String>("gate_formal_backend")
        .map(|s| s.as_str())
        .unwrap_or(GateFormalBackend::default().as_str());
    match GateFormalBackend::parse(value) {
        Ok(backend) => backend,
        Err(_) => {
            eprintln!("Invalid --gate-formal-backend: {:?}", value);
            std::process::exit(1);
        }
    }
}

fn parse_cut_db_rewrite_mode_or_exit(matches: &ArgMatches) -> CutDbRewriteMode {
    let value = matches
        .get_one::<String>("cut_db_rewrite_mode")
        .map(|s| s.as_str())
        .unwrap_or(CutDbRewriteMode::DEFAULT_CLI_VALUE);
    match CutDbRewriteMode::parse(value) {
        Some(mode) => mode,
        None => {
            eprintln!("Invalid --cut-db-rewrite-mode: {:?}", value);
            std::process::exit(1);
        }
    }
}

/// Parses the flags shared by post-gatification GateFn optimization clients.
pub(crate) fn parse_gate_fn_optimize_options(matches: &ArgMatches) -> GateFnOptimizeOptions {
    let defaults = CanonicalG8rOptions::default();
    let cut_db_rewrite = parse_bool(matches, "cut-db-rewrite", defaults.cut_db_rewrite);
    GateFnOptimizeOptions {
        fraig: parse_bool(matches, "fraig", defaults.fraig),
        reassociation: parse_bool(matches, "reassociation", defaults.reassociation),
        max_fraig_sim_samples: Some(parse_usize_or_exit(
            matches,
            "max_fraig_sim_samples",
            "--max-fraig-sim-samples",
            DEFAULT_MAX_FRAIG_SIM_SAMPLES,
        )),
        gate_formal_backend: parse_gate_formal_backend_or_exit(matches),
        cadical_terminate_limit: parse_u32_or_exit(
            matches,
            "cadical_terminate_limit",
            "--cadical-terminate-limit",
            DEFAULT_CADICAL_TERMINATE_LIMIT,
        ),
        cut_db: cut_db_rewrite.then(xlsynth_g8r::cut_db::loader::CutDb::load_default),
        cut_db_rewrite_max_iterations:
            xlsynth_g8r::cut_db_cli_defaults::CUT_DB_REWRITE_MAX_ITERATIONS_CLI,
        cut_db_rewrite_max_cuts_per_node:
            xlsynth_g8r::cut_db_cli_defaults::CUT_DB_REWRITE_MAX_CUTS_PER_NODE_CLI,
        cut_db_enable_large_cone_rewrite: parse_bool(
            matches,
            "cut-db-enable-large-cone-rewrite",
            defaults.cut_db_enable_large_cone_rewrite,
        ),
        cut_db_rewrite_mode: parse_cut_db_rewrite_mode_or_exit(matches),
    }
}

pub(crate) fn parse_g8r_cli_options(matches: &ArgMatches) -> CanonicalG8rOptions {
    let defaults = CanonicalG8rOptions::default();
    let fold = parse_bool(matches, "fold", defaults.fold);
    let hash = parse_bool(matches, "hash", defaults.hash);
    let track_pir_node_ids = parse_bool(matches, "track-pir-node-ids", defaults.track_pir_node_ids);
    let fraig = parse_bool(matches, "fraig", defaults.fraig);
    let reassociation = parse_bool(matches, "reassociation", defaults.reassociation);
    let enable_rewrite_carry_out = parse_bool(
        matches,
        "enable-rewrite-carry-out",
        defaults.enable_rewrite_carry_out,
    );
    let enable_rewrite_prio_encode = parse_bool(
        matches,
        "enable-rewrite-prio-encode",
        defaults.enable_rewrite_prio_encode,
    );
    let enable_rewrite_nary_add = parse_bool(
        matches,
        "enable-rewrite-nary-add",
        defaults.enable_rewrite_nary_add,
    );
    let enable_rewrite_mask_low = parse_bool(
        matches,
        "enable-rewrite-mask-low",
        defaults.enable_rewrite_mask_low,
    );
    let enable_rewrite_normalize_left = parse_bool(
        matches,
        "enable-rewrite-normalize-left",
        defaults.enable_rewrite_normalize_left,
    );
    let enable_formal_array_alias_analysis = parse_bool(
        matches,
        "enable-formal-array-alias-analysis",
        defaults.enable_formal_array_alias_analysis,
    );
    let unsafe_gatify_gate_operation = parse_bool(
        matches,
        "unsafe-gatify-gate-operation",
        defaults.unsafe_gatify_gate_operation,
    );
    let (adder_mapping, mul_adder_mapping) = parse_adder_mappings(matches);
    let toggle_sample_count =
        parse_usize_default(matches, "toggle_sample_count", defaults.toggle_sample_count);
    let toggle_sample_seed =
        parse_u64_default(matches, "toggle_sample_seed", defaults.toggle_sample_seed);
    let compute_graph_logical_effort = parse_bool(
        matches,
        "compute_graph_logical_effort",
        defaults.compute_graph_logical_effort,
    );
    let graph_logical_effort_beta1 = parse_f64_default(
        matches,
        "graph_logical_effort_beta1",
        defaults.graph_logical_effort_beta1,
    );
    let graph_logical_effort_beta2 = parse_f64_default(
        matches,
        "graph_logical_effort_beta2",
        defaults.graph_logical_effort_beta2,
    );
    let cut_db_rewrite = parse_bool(matches, "cut-db-rewrite", defaults.cut_db_rewrite);
    let cut_db_enable_large_cone_rewrite = parse_bool(
        matches,
        "cut-db-enable-large-cone-rewrite",
        defaults.cut_db_enable_large_cone_rewrite,
    );
    let cut_db_rewrite_mode = parse_cut_db_rewrite_mode_or_exit(matches);
    let max_fraig_sim_samples = parse_usize_or_exit(
        matches,
        "max_fraig_sim_samples",
        "--max-fraig-sim-samples",
        DEFAULT_MAX_FRAIG_SIM_SAMPLES,
    );
    let gate_formal_backend = parse_gate_formal_backend_or_exit(matches);
    let cadical_terminate_limit = parse_u32_or_exit(
        matches,
        "cadical_terminate_limit",
        "--cadical-terminate-limit",
        DEFAULT_CADICAL_TERMINATE_LIMIT,
    );

    CanonicalG8rOptions {
        fold,
        hash,
        track_pir_node_ids,
        enable_rewrite_carry_out,
        enable_rewrite_prio_encode,
        enable_rewrite_nary_add,
        enable_rewrite_mask_low,
        enable_rewrite_normalize_left,
        enable_formal_array_alias_analysis,
        unsafe_gatify_gate_operation,
        adder_mapping,
        mul_adder_mapping,
        fraig,
        reassociation,
        toggle_sample_count,
        toggle_sample_seed,
        compute_graph_logical_effort,
        graph_logical_effort_beta1,
        graph_logical_effort_beta2,
        cut_db_rewrite,
        cut_db_enable_large_cone_rewrite,
        cut_db_rewrite_mode,
        max_fraig_sim_samples,
        gate_formal_backend,
        cadical_terminate_limit,
    }
}

pub(crate) fn build_process_ir_path_options_for_cli(
    matches: &ArgMatches,
    quiet: bool,
    emit_netlist: bool,
    emit_independent_op_stats: bool,
    ir_top: Option<&str>,
    prepared_ir_out: Option<&std::path::Path>,
) -> process_ir_path::Options {
    let cli = parse_g8r_cli_options(matches);
    cli.to_process_ir_path_options(
        ir_top,
        quiet,
        emit_netlist,
        emit_independent_op_stats,
        prepared_ir_out,
    )
}