lamina 0.0.10

High-performance compiler backend for Lamina Intermediate Representation
Documentation
mod cli;

use cli::jit::handle_jit_compilation;
use cli::options::{parse_args, print_usage, toolchain_backends};
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::str::FromStr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let options = match parse_args() {
        Ok(opts) => opts,
        Err(e) => {
            if e == "help" {
                print_usage();
                return Ok(());
            } else {
                eprintln!("Error: {}", e);
                print_usage();
                std::process::exit(1);
            }
        }
    };

    let input_path = &options.input_file;
    if !input_path.exists() {
        eprintln!(
            "Error: Input file '{}' does not exist.",
            input_path.display()
        );
        std::process::exit(1);
    }

    if input_path.extension().is_none_or(|ext| ext != "lamina") {
        eprintln!(
            "Warning: Input file '{}' does not have .lamina extension.",
            input_path.display()
        );
    }

    let _exe_extension = if cfg!(windows) { ".exe" } else { "" };

    let target_for_extensions = if let Some(target_str) = &options.target_arch {
        lamina_platform::Target::from_str(target_str)
            .unwrap_or_else(|_| lamina_platform::Target::detect_host())
    } else {
        lamina_platform::Target::detect_host()
    };

    let output_stem = if let Some(out_path) = &options.output_file {
        out_path.clone()
    } else {
        let stem = input_path
            .file_stem()
            .map_or_else(|| "a".into(), PathBuf::from);
        if target_for_extensions.operating_system == lamina_platform::TargetOperatingSystem::Windows
            && stem.extension().is_none()
        {
            let mut stem_with_ext = stem.clone();
            stem_with_ext.set_extension("exe");
            stem_with_ext
        } else {
            stem
        }
    };

    let codegen_units = options.codegen_units.unwrap_or_else(|| {
        let max_threads = lamina_platform::cpu_count();
        if max_threads > 2 { max_threads - 2 } else { 1 }
    });

    if options.verbose {
        println!("[VERBOSE] Compiler options:");
        println!("  Verbose mode: {}", options.verbose);
        println!("  Optimization level: {}", options.opt_level);
        println!("  Codegen units (parallel threads): {}", codegen_units);
        if let Some(compiler) = &options.forced_compiler {
            println!("  Forced compiler: {}", compiler);
        }
        if !options.assembler_flags.is_empty() {
            println!(
                "  Additional assembler flags: {:?}",
                options.assembler_flags
            );
        }
        if !options.linker_flags.is_empty() {
            println!("  Additional linker flags: {:?}", options.linker_flags);
        }
    }

    let mut input_file = match File::open(input_path) {
        Ok(file) => file,
        Err(e) => {
            eprintln!("[ERROR] Failed to open input file: {}", e);
            std::process::exit(1);
        }
    };

    let mut ir_source = String::new();
    if let Err(e) = input_file.read_to_string(&mut ir_source) {
        eprintln!("[ERROR] Failed to read input file: {}", e);
        std::process::exit(1);
    }

    if options.emit_mir || options.emit_mir_asm.is_some() {
        let ir_mod = lamina::parser::parse_module(&ir_source)
            .map_err(|e| format!("IR parse failed: {}", e))?;
        let mut mir_mod =
            lamina::mir::codegen::from_ir(&ir_mod, input_path.to_string_lossy().as_ref())
                .map_err(|e| format!("MIR lowering failed: {}", e))?;

        if options.opt_level > 0 {
            let pipeline = lamina::mir::TransformPipeline::default_for_opt_level(options.opt_level);
            let transform_stats = pipeline
                .apply_to_module(&mut mir_mod)
                .map_err(|e| format!("MIR optimization failed: {}", e))?;

            let mut inlined_count = 0;
            if options.opt_level >= 2 {
                let inliner = lamina::mir::ModuleInlining::new();
                if let Ok(count) = inliner.inline_functions(&mut mir_mod) {
                    inlined_count = count;
                }
            }

            if options.verbose {
                println!(
                    "[VERBOSE] MIR optimizations: {} transforms run, {} made changes, {} functions inlined",
                    transform_stats.transforms_run,
                    transform_stats.transforms_changed,
                    inlined_count
                );
            }
        }
        if options.emit_mir {
            let mut mir_path = output_stem.clone();
            mir_path.set_extension("lumir");
            let mut mir_file =
                File::create(&mir_path).map_err(|e| format!("Failed to create MIR file: {}", e))?;
            let mir_text = format!("{}", mir_mod);
            mir_file
                .write_all(mir_text.as_bytes())
                .map_err(|e| format!("Failed to write MIR file: {}", e))?;
            println!("[INFO] MIR written to {}", mir_path.display());
        }

        if options.emit_mir_asm.is_some() {
            let default_target = lamina_platform::Target::detect_host();
            let default_target_str = default_target.to_str();
            let target_str = options
                .target_arch
                .as_deref()
                .unwrap_or(&default_target_str);
            let target = lamina_platform::Target::from_str(target_str)
                .unwrap_or_else(|_| lamina_platform::Target::detect_host());

            let asm_extension = match target.architecture {
                lamina_platform::TargetArchitecture::Wasm32
                | lamina_platform::TargetArchitecture::Wasm64 => "wat",
                _ => {
                    if target.operating_system == lamina_platform::TargetOperatingSystem::Windows {
                        "asm"
                    } else {
                        "s"
                    }
                }
            };
            let mut mir_asm_path = output_stem.clone();
            mir_asm_path.set_extension(asm_extension);

            let mut out = Vec::<u8>::new();
            let target_os = target.operating_system;

            lamina::mir_codegen::generate_mir_to_target_with_settings(
                &mir_mod,
                &mut out,
                target.architecture,
                target_os,
                codegen_units,
                &options.mir_codegen_settings,
            )
            .map_err(|e| format!("MIR→{} emission failed: {}", target.architecture, e))?;

            let arch_name = match target.architecture {
                lamina_platform::TargetArchitecture::X86_64 => "x86_64",
                lamina_platform::TargetArchitecture::Wasm32
                | lamina_platform::TargetArchitecture::Wasm64 => "WASM",
                lamina_platform::TargetArchitecture::Aarch64 => "AArch64",
                lamina_platform::TargetArchitecture::Arx64 => "ARX64",
                lamina_platform::TargetArchitecture::Riscv32 => "RISC-V32",
                lamina_platform::TargetArchitecture::Riscv64 => "RISC-V64",
                #[cfg(feature = "nightly")]
                lamina_platform::TargetArchitecture::Riscv128 => "RISC-V128",
                _ => "unknown",
            };

            println!(
                "[INFO] MIR {} asm (experimental) written to {}",
                arch_name,
                mir_asm_path.display()
            );

            File::create(&mir_asm_path)
                .and_then(|mut f| f.write_all(&out))
                .map_err(|e| format!("Failed to write MIR output: {}", e))?;
        }

        return Ok(());
    }

    if options.jit {
        return handle_jit_compilation(&ir_source, input_path, &options);
    }

    if !options.emit_mir {
        let target = if let Some(target_str) = &options.target_arch {
            if options.verbose {
                println!("[VERBOSE] Using explicit target: {}", target_str);
            }
            lamina_platform::Target::from_str(target_str).unwrap_or_else(|e| {
                eprintln!(
                    "Warning: Invalid target '{}': {}. Using host target.",
                    target_str, e
                );
                lamina_platform::Target::detect_host()
            })
        } else {
            let default_target = lamina_platform::Target::detect_host();
            if options.verbose {
                println!("[VERBOSE] Using host target: {}", default_target);
            }
            default_target
        };

        let ir_mod = lamina::parser::parse_module(&ir_source)
            .map_err(|e| format!("IR parse failed: {}", e))?;
        let mut mir_mod =
            lamina::mir::codegen::from_ir(&ir_mod, input_path.to_string_lossy().as_ref())
                .map_err(|e| format!("MIR lowering failed: {}", e))?;

        if options.opt_level > 0 {
            let pipeline = lamina::mir::TransformPipeline::default_for_opt_level(options.opt_level);
            let transform_stats = pipeline
                .apply_to_module(&mut mir_mod)
                .map_err(|e| format!("MIR optimization failed: {}", e))?;

            if options.verbose {
                println!(
                    "[VERBOSE] MIR optimizations: {} transforms run, {} made changes",
                    transform_stats.transforms_run, transform_stats.transforms_changed
                );
            }
        }

        let mut intermediate_buffer = Vec::<u8>::new();
        lamina::mir_codegen::generate_mir_to_target_with_settings(
            &mir_mod,
            &mut intermediate_buffer,
            target.architecture,
            target.operating_system,
            codegen_units,
            &options.mir_codegen_settings,
        )
        .map_err(|e| format!("Code generation failed: {}", e))?;

        let intermediate_ext = lamina::mir_codegen::assemble::get_intermediate_extension(
            target.architecture,
            target.operating_system,
        );
        let mut intermediate_path = output_stem.clone();
        intermediate_path.set_extension(intermediate_ext);

        let final_ext = lamina::mir_codegen::link::get_output_extension(
            target.architecture,
            target.operating_system,
        );
        let mut final_output_display = output_stem.clone();
        if !final_ext.is_empty() {
            final_output_display.set_extension(final_ext);
        }

        let intermediate_name = if matches!(
            target.architecture,
            lamina_platform::TargetArchitecture::Wasm32
                | lamina_platform::TargetArchitecture::Wasm64
        ) {
            println!(
                "[INFO] Compiling {} -> {}",
                input_path.display(),
                final_output_display.display()
            );
            "WAT"
        } else {
            println!(
                "[INFO] Compiling {} -> {} -> {}",
                input_path.display(),
                intermediate_path.display(),
                final_output_display.display()
            );
            "Assembly"
        };

        File::create(&intermediate_path)
            .and_then(|mut f| f.write_all(&intermediate_buffer))
            .map_err(|e| format!("Failed to write intermediate file: {}", e))?;

        println!(
            "[INFO] {} generated successfully ({} bytes).",
            intermediate_name,
            intermediate_buffer.len()
        );
        println!(
            "[INFO] {} written to {}",
            intermediate_name,
            intermediate_path.display()
        );

        if options.emit_asm_only {
            println!("[INFO] Skipping assembly and linking as requested (--emit-asm flag)");
            return Ok(());
        }

        let assembly_output_ext =
            lamina::mir_codegen::assemble::get_assembly_output_extension(target.architecture);
        let mut assembly_output_path = output_stem.clone();
        assembly_output_path.set_extension(assembly_output_ext);

        let (assembler_backend, linker_backend) =
            toolchain_backends(&options.forced_compiler, &options.assembler);
        let assemble_result = lamina::mir_codegen::assemble::assemble_with_ras_object_options(
            &intermediate_path,
            &assembly_output_path,
            target.architecture,
            target.operating_system,
            assembler_backend,
            &options.assembler_flags,
            options.verbose,
            options.ras_object_write_options(target.operating_system),
        )
        .map_err(|e| format!("Assembly failed: {}", e))?;

        println!(
            "[INFO] {} assembled successfully.",
            if matches!(
                target.architecture,
                lamina_platform::TargetArchitecture::Wasm32
                    | lamina_platform::TargetArchitecture::Wasm64
            ) {
                "WASM binary"
            } else {
                "Object file"
            }
        );

        if assemble_result.needs_linking {
            let final_output_ext = lamina::mir_codegen::link::get_output_extension(
                target.architecture,
                target.operating_system,
            );
            let mut final_output_path = output_stem.clone();
            if !final_output_ext.is_empty() {
                final_output_path.set_extension(final_output_ext);
            }

            lamina::mir_codegen::link::link(
                &assemble_result.output_path,
                &final_output_path,
                target.architecture,
                target.operating_system,
                linker_backend,
                &options.linker_flags,
                options.verbose,
            )
            .map_err(|e| format!("Linking failed: {}", e))?;

            println!(
                "[INFO] Executable '{}' created successfully.",
                final_output_path.display()
            );
        } else {
            println!(
                "[INFO] {} '{}' created successfully.",
                if matches!(
                    target.architecture,
                    lamina_platform::TargetArchitecture::Wasm32
                        | lamina_platform::TargetArchitecture::Wasm64
                ) {
                    "WASM binary"
                } else {
                    "Binary"
                },
                assembly_output_path.display()
            );
        }
    }

    Ok(())
}