//! Hexagon HVX Code Generator
//!
//! This generator creates v64.rs and v128.rs from scratch using the LLVM HVX
//! header file as the sole source of truth. It parses the C intrinsic prototypes
//! and generates Rust wrapper functions with appropriate attributes.
//!
//! The two generated files provide:
//! - v64.rs: 64-byte vector mode intrinsics (512-bit vectors)
//! - v128.rs: 128-byte vector mode intrinsics (1024-bit vectors)
//!
//! Both modules are available unconditionally, but require the appropriate
//! target features to actually use the intrinsics.
//!
//! Usage:
//!     cd crates/stdarch-gen-hexagon
//!     cargo run
//!     # Output is written to ../core_arch/src/hexagon/v64.rs and v128.rs

use regex::Regex;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Write;
use std::path::Path;

/// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics.
/// These intrinsics have equivalent semantics and can be lowered to the generic form.
fn get_simd_intrinsic_mappings() -> HashMap<&'static str, &'static str> {
    let mut map = HashMap::new();
    // Bitwise operations (element-size independent)
    map.insert("vxor", "simd_xor");
    map.insert("vand", "simd_and");
    map.insert("vor", "simd_or");
    // Word (32-bit) arithmetic operations
    map.insert("vaddw", "simd_add");
    map.insert("vsubw", "simd_sub");
    map
}

/// The tracking issue number for the stdarch_hexagon feature
const TRACKING_ISSUE: &str = "151523";

/// HVX vector length mode
#[derive(Debug, Clone, Copy, PartialEq)]
enum VectorMode {
    /// 64-byte vectors (512 bits)
    V64,
    /// 128-byte vectors (1024 bits)
    V128,
}

impl VectorMode {
    fn bytes(&self) -> u32 {
        match self {
            VectorMode::V64 => 64,
            VectorMode::V128 => 128,
        }
    }

    fn bits(&self) -> u32 {
        self.bytes() * 8
    }

    fn lanes(&self) -> u32 {
        self.bytes() / 4 // 32-bit lanes
    }

    fn target_feature(&self) -> &'static str {
        match self {
            VectorMode::V64 => "hvx-length64b",
            VectorMode::V128 => "hvx-length128b",
        }
    }
}

/// LLVM version the header file is from (for reference)
/// Source: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/clang/lib/Headers/hvx_hexagon_protos.h
const LLVM_VERSION: &str = "22.1.0-rc1";

/// Maximum HVX architecture version supported by rustc
/// Check with: rustc --target=hexagon-unknown-linux-musl --print target-features
const MAX_SUPPORTED_ARCH: u32 = 79;

/// Local header file path (checked into the repository)
const HEADER_FILE: &str = "hvx_hexagon_protos.h";

/// Intrinsic information parsed from the LLVM header
#[derive(Debug, Clone)]
struct IntrinsicInfo {
    /// The Q6_* intrinsic name (e.g., "Q6_V_vadd_VV")
    q6_name: String,
    /// The LLVM builtin name without prefix (e.g., "V6_vaddb")
    builtin_name: String,
    /// The short instruction name for assert_instr (e.g., "vaddb")
    instr_name: String,
    /// The assembly syntax from the comment
    asm_syntax: String,
    /// Instruction type
    instr_type: String,
    /// Execution slots
    exec_slots: String,
    /// Minimum HVX architecture version required
    min_arch: u32,
    /// Return type
    return_type: RustType,
    /// Parameters (name, type)
    params: Vec<(String, RustType)>,
    /// Whether this is a compound intrinsic (multiple builtins)
    is_compound: bool,
    /// For compound intrinsics: the parsed expression tree
    compound_expr: Option<CompoundExpr>,
}

/// Expression tree for compound intrinsics
#[derive(Debug, Clone)]
enum CompoundExpr {
    /// A call to a builtin: (builtin_name without V6_ prefix, arguments)
    BuiltinCall(String, Vec<CompoundExpr>),
    /// A parameter reference by name
    Param(String),
    /// An integer literal (like -1)
    IntLiteral(i32),
}

/// Rust type mappings
#[derive(Debug, Clone, PartialEq)]
enum RustType {
    HvxVector,
    HvxVectorPair,
    HvxVectorPred,
    I32,
    MutPtrHvxVector,
    Unit,
}

impl RustType {
    fn from_c_type(c_type: &str) -> Option<Self> {
        match c_type.trim() {
            "HVX_Vector" => Some(RustType::HvxVector),
            "HVX_VectorPair" => Some(RustType::HvxVectorPair),
            "HVX_VectorPred" => Some(RustType::HvxVectorPred),
            "Word32" => Some(RustType::I32),
            "HVX_Vector*" => Some(RustType::MutPtrHvxVector),
            "void" => Some(RustType::Unit),
            _ => None,
        }
    }

    fn to_rust_str(&self) -> &'static str {
        match self {
            RustType::HvxVector => "HvxVector",
            RustType::HvxVectorPair => "HvxVectorPair",
            RustType::HvxVectorPred => "HvxVectorPred",
            RustType::I32 => "i32",
            RustType::MutPtrHvxVector => "*mut HvxVector",
            RustType::Unit => "()",
        }
    }

    fn to_extern_str(&self) -> &'static str {
        match self {
            RustType::HvxVector => "HvxVector",
            RustType::HvxVectorPair => "HvxVectorPair",
            RustType::HvxVectorPred => "HvxVectorPred",
            RustType::I32 => "i32",
            RustType::MutPtrHvxVector => "*mut HvxVector",
            RustType::Unit => "()",
        }
    }
}

/// Parse a compound macro expression into an expression tree
fn parse_compound_expr(expr: &str) -> Option<CompoundExpr> {
    let expr = expr.trim();

    // Try to match an integer literal (like -1)
    if let Ok(n) = expr.parse::<i32>() {
        return Some(CompoundExpr::IntLiteral(n));
    }

    // Try to match a simple parameter name (Vu, Vv, Rt, Qs, Qt, Qx, Vx, etc.)
    // These are typically short identifiers in the macro
    if expr.len() <= 3
        && expr.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
        && !expr.contains("__")
    {
        return Some(CompoundExpr::Param(expr.to_lowercase()));
    }

    // Check if it's wrapped in extra parens first
    if expr.starts_with('(') && expr.ends_with(')') {
        // Check if these parens wrap the entire expression
        let inner = &expr[1..expr.len() - 1];
        // Count depth: if after removing outer parens the expression is balanced,
        // the outer parens were enclosing everything
        if is_balanced_parens(inner) {
            // But we also need to verify these aren't part of a function call
            // If the inner expression is balanced and the whole thing starts with (
            // and ends with ), it's a paren wrapper
            let result = parse_compound_expr(inner);
            if result.is_some() {
                return result;
            }
        }
    }

    // Try to match __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args)
    // The args portion may contain nested calls, so we need to find the matching paren
    if expr.starts_with("__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_") {
        // Find the end of the builtin name (after V6_)
        let prefix = "__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_";
        let after_prefix = &expr[prefix.len()..];
        if let Some(paren_pos) = after_prefix.find(')') {
            let builtin_name = &after_prefix[..paren_pos];
            let rest = &after_prefix[paren_pos + 1..]; // Skip the closing ) of the WRAP
                                                       // rest should now be "(args)"
            if rest.starts_with('(') && rest.ends_with(')') {
                let args_str = &rest[1..rest.len() - 1];
                let args = parse_compound_args(args_str)?;
                return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args));
            }
        }
    }

    // Try to match __builtin_HEXAGON_V6_xxx(args) without wrap
    if expr.starts_with("__builtin_HEXAGON_V6_") {
        let prefix = "__builtin_HEXAGON_V6_";
        let after_prefix = &expr[prefix.len()..];
        if let Some(paren_pos) = after_prefix.find('(') {
            let builtin_name = &after_prefix[..paren_pos];
            let rest = &after_prefix[paren_pos..];
            if rest.starts_with('(') && rest.ends_with(')') {
                let args_str = &rest[1..rest.len() - 1];
                let args = parse_compound_args(args_str)?;
                return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args));
            }
        }
    }

    None
}

/// Check if parentheses are balanced in a string
fn is_balanced_parens(s: &str) -> bool {
    let mut depth = 0;
    for c in s.chars() {
        match c {
            '(' => depth += 1,
            ')' => {
                depth -= 1;
                if depth < 0 {
                    return false;
                }
            }
            _ => {}
        }
    }
    depth == 0
}

/// Parse comma-separated arguments, respecting nested parentheses
fn parse_compound_args(args_str: &str) -> Option<Vec<CompoundExpr>> {
    let mut args = Vec::new();
    let mut current = String::new();
    let mut depth = 0;

    for c in args_str.chars() {
        match c {
            '(' => {
                depth += 1;
                current.push(c);
            }
            ')' => {
                depth -= 1;
                current.push(c);
            }
            ',' if depth == 0 => {
                let arg = current.trim().to_string();
                if !arg.is_empty() {
                    args.push(parse_compound_expr(&arg)?);
                }
                current.clear();
            }
            _ => current.push(c),
        }
    }

    // Don't forget the last argument
    let arg = current.trim().to_string();
    if !arg.is_empty() {
        args.push(parse_compound_expr(&arg)?);
    }

    Some(args)
}

/// Extract all builtin names used in a compound expression
fn collect_builtins_from_expr(expr: &CompoundExpr, builtins: &mut HashSet<String>) {
    match expr {
        CompoundExpr::BuiltinCall(name, args) => {
            builtins.insert(name.clone());
            for arg in args {
                collect_builtins_from_expr(arg, builtins);
            }
        }
        CompoundExpr::Param(_) | CompoundExpr::IntLiteral(_) => {}
    }
}

/// Read the local HVX header file
fn read_header(crate_dir: &Path) -> Result<String, String> {
    let header_path = crate_dir.join(HEADER_FILE);
    println!("Reading HVX header from: {}", header_path.display());
    println!("  (LLVM version: {})", LLVM_VERSION);

    std::fs::read_to_string(&header_path).map_err(|e| {
        format!(
            "Failed to read header file {}: {}",
            header_path.display(),
            e
        )
    })
}

/// Parse a C function prototype to extract return type and parameters
fn parse_prototype(prototype: &str) -> Option<(RustType, Vec<(String, RustType)>)> {
    // Pattern: ReturnType FunctionName(ParamType1 Param1, ParamType2 Param2, ...)
    let proto_re = Regex::new(r"(\w+(?:\*)?)\s+Q6_\w+\(([^)]*)\)").unwrap();

    if let Some(caps) = proto_re.captures(prototype) {
        let return_type_str = caps[1].trim();
        let params_str = &caps[2];

        let return_type = RustType::from_c_type(return_type_str)?;

        let mut params = Vec::new();
        if !params_str.trim().is_empty() {
            // Pattern: Type Name or Type* Name
            let param_re = Regex::new(r"(\w+\*?)\s+(\w+)").unwrap();
            for param in params_str.split(',') {
                let param = param.trim();
                if let Some(pcaps) = param_re.captures(param) {
                    let ptype_str = pcaps[1].trim();
                    let pname = pcaps[2].to_lowercase();
                    if let Some(ptype) = RustType::from_c_type(ptype_str) {
                        params.push((pname, ptype));
                    } else {
                        return None; // Unknown type
                    }
                }
            }
        }

        Some((return_type, params))
    } else {
        None
    }
}

/// Parse the LLVM header file to extract intrinsic information
fn parse_header(content: &str) -> Vec<IntrinsicInfo> {
    let mut intrinsics = Vec::new();

    let arch_re = Regex::new(r"#if __HVX_ARCH__ >= (\d+)").unwrap();

    // Regex to extract the simple builtin name from a macro body
    // Match: __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args)
    let simple_builtin_re =
        Regex::new(r"__BUILTIN_VECTOR_WRAP\(__builtin_HEXAGON_(\w+)\)\([^)]*\)\s*$").unwrap();

    // Also handle builtins without VECTOR_WRAP
    let simple_builtin_re2 = Regex::new(r"__builtin_HEXAGON_(\w+)\([^)]*\)\s*$").unwrap();

    // Regex to extract Q6 name from #define
    let q6_name_re = Regex::new(r"#define\s+(Q6_\w+)").unwrap();

    // Regex to extract macro expression body
    let macro_expr_re = Regex::new(r"#define\s+Q6_\w+\([^)]*\)\s+(.+)").unwrap();

    let lines: Vec<&str> = content.lines().collect();
    let mut current_arch: u32 = 60;
    let mut i = 0;

    while i < lines.len() {
        // Track architecture version
        if let Some(caps) = arch_re.captures(lines[i]) {
            if let Ok(arch) = caps[1].parse() {
                current_arch = arch;
            }
        }

        // Look for Assembly Syntax comment block
        if lines[i].contains("Assembly Syntax:") {
            let mut asm_syntax = String::new();
            let mut prototype = String::new();
            let mut instr_type = String::new();
            let mut exec_slots = String::new();

            // Parse the comment block
            let mut j = i;
            while j < lines.len() && !lines[j].starts_with("#define") {
                let line = lines[j];
                if line.contains("Assembly Syntax:") {
                    if let Some(pos) = line.find("Assembly Syntax:") {
                        asm_syntax = line[pos + 16..].trim().to_string();
                    }
                } else if line.contains("C Intrinsic Prototype:") {
                    if let Some(pos) = line.find("C Intrinsic Prototype:") {
                        prototype = line[pos + 22..].trim().to_string();
                    }
                } else if line.contains("Instruction Type:") {
                    if let Some(pos) = line.find("Instruction Type:") {
                        instr_type = line[pos + 17..].trim().to_string();
                    }
                } else if line.contains("Execution Slots:") {
                    if let Some(pos) = line.find("Execution Slots:") {
                        exec_slots = line[pos + 16..].trim().to_string();
                    }
                }
                j += 1;
            }

            // Now find the #define line
            while j < lines.len() && !lines[j].starts_with("#define") {
                j += 1;
            }

            if j < lines.len() {
                let define_line = lines[j];

                // Extract Q6 name and check if it's simple or compound
                if let Some(caps) = q6_name_re.captures(define_line) {
                    let q6_name = caps[1].to_string();

                    // Get the full macro body (handle line continuations)
                    let mut macro_body = define_line.to_string();
                    let mut k = j;
                    while macro_body.trim_end().ends_with('\\') && k + 1 < lines.len() {
                        k += 1;
                        macro_body.push_str(lines[k]);
                    }

                    // Try to extract simple builtin name
                    let builtin_name = simple_builtin_re
                        .captures(&macro_body)
                        .or_else(|| simple_builtin_re2.captures(&macro_body))
                        .map(|bcaps| bcaps[1].to_string());

                    // Check if it's a compound intrinsic (multiple __builtin calls)
                    let builtin_count = macro_body.matches("__builtin_HEXAGON_").count();
                    let is_compound = builtin_count > 1;

                    // Parse prototype
                    if let Some((return_type, params)) = parse_prototype(&prototype) {
                        if is_compound {
                            // For compound intrinsics, parse the expression
                            // Extract the macro body after the parameter list
                            if let Some(expr_caps) = macro_expr_re.captures(&macro_body) {
                                let expr_str = expr_caps[1].trim().replace(['\n', '\\'], " ");
                                let expr_str = expr_str.trim();

                                if let Some(compound_expr) = parse_compound_expr(expr_str) {
                                    // For compound intrinsics, we use the outermost builtin
                                    // as the "primary" for the instruction name
                                    let (primary_builtin, instr_name) = match &compound_expr {
                                        CompoundExpr::BuiltinCall(name, _) => {
                                            (name.clone(), name.clone())
                                        }
                                        _ => continue,
                                    };

                                    intrinsics.push(IntrinsicInfo {
                                        q6_name,
                                        builtin_name: format!("V6_{}", primary_builtin),
                                        instr_name,
                                        asm_syntax,
                                        instr_type,
                                        exec_slots,
                                        min_arch: current_arch,
                                        return_type,
                                        params,
                                        is_compound: true,
                                        compound_expr: Some(compound_expr),
                                    });
                                }
                            }
                        } else if let Some(builtin) = builtin_name {
                            // Extract short instruction name
                            let instr_name = builtin
                                .strip_prefix("V6_")
                                .map(|s| s.to_string())
                                .unwrap_or_else(|| builtin.clone());

                            intrinsics.push(IntrinsicInfo {
                                q6_name,
                                builtin_name: builtin,
                                instr_name,
                                asm_syntax,
                                instr_type,
                                exec_slots,
                                min_arch: current_arch,
                                return_type,
                                params,
                                is_compound: false,
                                compound_expr: None,
                            });
                        }
                    }
                }
            }
            i = j;
        }
        i += 1;
    }

    intrinsics
}

/// Generate the module documentation
fn generate_module_doc(mode: VectorMode) -> String {
    format!(
        r#"//! Hexagon HVX {bytes}-byte vector mode intrinsics
//!
//! This module provides intrinsics for the Hexagon Vector Extensions (HVX)
//! in {bytes}-byte vector mode ({bits}-bit vectors).
//!
//! HVX is a wide vector extension designed for high-performance signal processing.
//! [Hexagon HVX Programmer's Reference Manual](https://docs.qualcomm.com/doc/80-N2040-61)
//!
//! ## Vector Types
//!
//! In {bytes}-byte mode:
//! - `HvxVector` is {bits} bits ({bytes} bytes) containing {lanes} x 32-bit values
//! - `HvxVectorPair` is {pair_bits} bits ({pair_bytes} bytes)
//! - `HvxVectorPred` is {bits} bits ({bytes} bytes) for predicate operations
//!
//! To use this module, compile with `-C target-feature=+{target_feature}`.
//!
//! ## Naming Convention
//!
//! Function names preserve the original Q6 naming case because the convention
//! uses case to distinguish register types:
//! - `W` (uppercase) = vector pair (`HvxVectorPair`)
//! - `V` (uppercase) = vector (`HvxVector`)
//! - `Q` (uppercase) = predicate (`HvxVectorPred`)
//! - `R` = scalar register (`i32`)
//!
//! For example, `Q6_W_vcombine_VV` operates on a vector pair while
//! `Q6_V_hi_W` extracts a vector from a pair.
//!
//! ## Architecture Versions
//!
//! Different intrinsics require different HVX architecture versions. Use the
//! appropriate target feature to enable the required version:
//! - HVX v60: `-C target-feature=+hvxv60` (basic HVX operations)
//! - HVX v62: `-C target-feature=+hvxv62`
//! - HVX v65: `-C target-feature=+hvxv65` (includes floating-point support)
//! - HVX v66: `-C target-feature=+hvxv66`
//! - HVX v68: `-C target-feature=+hvxv68`
//! - HVX v69: `-C target-feature=+hvxv69`
//! - HVX v73: `-C target-feature=+hvxv73`
//! - HVX v79: `-C target-feature=+hvxv79`
//!
//! Each version includes all features from previous versions.
"#,
        bytes = mode.bytes(),
        bits = mode.bits(),
        lanes = mode.lanes(),
        pair_bytes = mode.bytes() * 2,
        pair_bits = mode.bits() * 2,
        target_feature = mode.target_feature(),
    )
}

/// Generate the type definitions for a specific vector mode
fn generate_types(mode: VectorMode) -> String {
    let lanes = mode.lanes();
    let pair_lanes = lanes * 2;
    let bits = mode.bits();
    let bytes = mode.bytes();
    let pair_bits = bits * 2;
    let pair_bytes = bytes * 2;

    format!(
        r#"
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

#[cfg(test)]
use stdarch_test::assert_instr;

use crate::intrinsics::simd::{{simd_add, simd_and, simd_or, simd_sub, simd_xor}};

// HVX type definitions for {bytes}-byte vector mode
types! {{
    #![unstable(feature = "stdarch_hexagon", issue = "{TRACKING_ISSUE}")]

    /// HVX vector type ({bits} bits / {bytes} bytes)
    ///
    /// This type represents a single HVX vector register containing {lanes} x 32-bit values.
    pub struct HvxVector({lanes} x i32);

    /// HVX vector pair type ({pair_bits} bits / {pair_bytes} bytes)
    ///
    /// This type represents a pair of HVX vector registers, often used for
    /// operations that produce double-width results.
    pub struct HvxVectorPair({pair_lanes} x i32);

    /// HVX vector predicate type ({bits} bits / {bytes} bytes)
    ///
    /// This type represents a predicate vector used for conditional operations.
    /// Each bit corresponds to a lane in the vector.
    pub struct HvxVectorPred({lanes} x i32);
}}
"#,
        bytes = bytes,
        bits = bits,
        lanes = lanes,
        pair_bits = pair_bits,
        pair_bytes = pair_bytes,
        pair_lanes = pair_lanes,
        TRACKING_ISSUE = TRACKING_ISSUE,
    )
}

/// Builtin signature information for extern declarations
struct BuiltinSignature {
    /// The V6_ prefixed name
    full_name: String,
    /// The short name (without V6_)
    short_name: String,
    /// Return type
    return_type: RustType,
    /// Parameter types
    param_types: Vec<RustType>,
}

/// Get known signatures for builtins used in compound operations
/// These are the helper builtins that don't have their own Q6_ wrapper
fn get_compound_helper_signatures() -> HashMap<String, BuiltinSignature> {
    let mut map = HashMap::new();

    // vandvrt: HVX_Vector -> i32 -> HVX_Vector
    // Converts predicate to vector representation. LLVM uses HVX_Vector for both.
    map.insert(
        "vandvrt".to_string(),
        BuiltinSignature {
            full_name: "V6_vandvrt".to_string(),
            short_name: "vandvrt".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::I32],
        },
    );

    // vandqrt: HVX_Vector -> i32 -> HVX_Vector
    // Converts vector representation back to predicate. LLVM uses HVX_Vector for both.
    map.insert(
        "vandqrt".to_string(),
        BuiltinSignature {
            full_name: "V6_vandqrt".to_string(),
            short_name: "vandqrt".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::I32],
        },
    );

    // vandvrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector
    map.insert(
        "vandvrt_acc".to_string(),
        BuiltinSignature {
            full_name: "V6_vandvrt_acc".to_string(),
            short_name: "vandvrt_acc".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32],
        },
    );

    // vandqrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector
    map.insert(
        "vandqrt_acc".to_string(),
        BuiltinSignature {
            full_name: "V6_vandqrt_acc".to_string(),
            short_name: "vandqrt_acc".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32],
        },
    );

    // pred_and: HVX_Vector -> HVX_Vector -> HVX_Vector
    map.insert(
        "pred_and".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_and".to_string(),
            short_name: "pred_and".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // pred_and_n: HVX_Vector -> HVX_Vector -> HVX_Vector
    map.insert(
        "pred_and_n".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_and_n".to_string(),
            short_name: "pred_and_n".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // pred_or: HVX_Vector -> HVX_Vector -> HVX_Vector
    map.insert(
        "pred_or".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_or".to_string(),
            short_name: "pred_or".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // pred_or_n: HVX_Vector -> HVX_Vector -> HVX_Vector
    map.insert(
        "pred_or_n".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_or_n".to_string(),
            short_name: "pred_or_n".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // pred_xor: HVX_Vector -> HVX_Vector -> HVX_Vector
    map.insert(
        "pred_xor".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_xor".to_string(),
            short_name: "pred_xor".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // pred_not: HVX_Vector -> HVX_Vector
    map.insert(
        "pred_not".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_not".to_string(),
            short_name: "pred_not".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector],
        },
    );

    // pred_scalar2: i32 -> HVX_Vector
    map.insert(
        "pred_scalar2".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_scalar2".to_string(),
            short_name: "pred_scalar2".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::I32],
        },
    );

    // Conditional store operations
    map.insert(
        "vS32b_qpred_ai".to_string(),
        BuiltinSignature {
            full_name: "V6_vS32b_qpred_ai".to_string(),
            short_name: "vS32b_qpred_ai".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vS32b_nqpred_ai".to_string(),
        BuiltinSignature {
            full_name: "V6_vS32b_nqpred_ai".to_string(),
            short_name: "vS32b_nqpred_ai".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vS32b_nt_qpred_ai".to_string(),
        BuiltinSignature {
            full_name: "V6_vS32b_nt_qpred_ai".to_string(),
            short_name: "vS32b_nt_qpred_ai".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vS32b_nt_nqpred_ai".to_string(),
        BuiltinSignature {
            full_name: "V6_vS32b_nt_nqpred_ai".to_string(),
            short_name: "vS32b_nt_nqpred_ai".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
            ],
        },
    );

    // Conditional accumulation operations
    for (suffix, _elem) in [("b", "byte"), ("h", "halfword"), ("w", "word")] {
        // vaddbq, vaddhq, vaddwq
        map.insert(
            format!("vadd{}q", suffix),
            BuiltinSignature {
                full_name: format!("V6_vadd{}q", suffix),
                short_name: format!("vadd{}q", suffix),
                return_type: RustType::HvxVector,
                param_types: vec![
                    RustType::HvxVector,
                    RustType::HvxVector,
                    RustType::HvxVector,
                ],
            },
        );
        // vaddbnq, vaddhnq, vaddwnq
        map.insert(
            format!("vadd{}nq", suffix),
            BuiltinSignature {
                full_name: format!("V6_vadd{}nq", suffix),
                short_name: format!("vadd{}nq", suffix),
                return_type: RustType::HvxVector,
                param_types: vec![
                    RustType::HvxVector,
                    RustType::HvxVector,
                    RustType::HvxVector,
                ],
            },
        );
    }

    // Comparison operations with accumulation
    // veqb_and, veqb_or, veqb_xor, etc.
    for elem in ["b", "h", "w", "ub", "uh", "uw"] {
        for op in ["and", "or", "xor"] {
            // veq*_and, veq*_or, veq*_xor
            map.insert(
                format!("veq{}_{}", elem, op),
                BuiltinSignature {
                    full_name: format!("V6_veq{}_{}", elem, op),
                    short_name: format!("veq{}_{}", elem, op),
                    return_type: RustType::HvxVector,
                    param_types: vec![
                        RustType::HvxVector,
                        RustType::HvxVector,
                        RustType::HvxVector,
                    ],
                },
            );
            // vgt*_and, vgt*_or, vgt*_xor
            map.insert(
                format!("vgt{}_{}", elem, op),
                BuiltinSignature {
                    full_name: format!("V6_vgt{}_{}", elem, op),
                    short_name: format!("vgt{}_{}", elem, op),
                    return_type: RustType::HvxVector,
                    param_types: vec![
                        RustType::HvxVector,
                        RustType::HvxVector,
                        RustType::HvxVector,
                    ],
                },
            );
        }
    }

    // Floating-point comparison operations (hf = half-float, sf = single-float)
    for elem in ["hf", "sf"] {
        // Basic comparison: vgt*
        map.insert(
            format!("vgt{}", elem),
            BuiltinSignature {
                full_name: format!("V6_vgt{}", elem),
                short_name: format!("vgt{}", elem),
                return_type: RustType::HvxVector,
                param_types: vec![RustType::HvxVector, RustType::HvxVector],
            },
        );

        for op in ["and", "or", "xor"] {
            // vgt*_and, vgt*_or, vgt*_xor
            map.insert(
                format!("vgt{}_{}", elem, op),
                BuiltinSignature {
                    full_name: format!("V6_vgt{}_{}", elem, op),
                    short_name: format!("vgt{}_{}", elem, op),
                    return_type: RustType::HvxVector,
                    param_types: vec![
                        RustType::HvxVector,
                        RustType::HvxVector,
                        RustType::HvxVector,
                    ],
                },
            );
        }
    }

    // Prefix operations with predicate
    for elem in ["b", "h", "w"] {
        map.insert(
            format!("vprefixq{}", elem),
            BuiltinSignature {
                full_name: format!("V6_vprefixq{}", elem),
                short_name: format!("vprefixq{}", elem),
                return_type: RustType::HvxVector,
                param_types: vec![RustType::HvxVector],
            },
        );
    }

    // Scatter operations with predicate
    map.insert(
        "vscattermhq".to_string(),
        BuiltinSignature {
            full_name: "V6_vscattermhq".to_string(),
            short_name: "vscattermhq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVector,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vscattermhwq".to_string(),
        BuiltinSignature {
            full_name: "V6_vscattermhwq".to_string(),
            short_name: "vscattermhwq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVectorPair,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vscattermwq".to_string(),
        BuiltinSignature {
            full_name: "V6_vscattermwq".to_string(),
            short_name: "vscattermwq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVector,
                RustType::HvxVector,
            ],
        },
    );

    // Add with carry saturation
    map.insert(
        "vaddcarrysat".to_string(),
        BuiltinSignature {
            full_name: "V6_vaddcarrysat".to_string(),
            short_name: "vaddcarrysat".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![
                RustType::HvxVector,
                RustType::HvxVector,
                RustType::HvxVector,
            ],
        },
    );

    // Gather operations with predicate
    map.insert(
        "vgathermhq".to_string(),
        BuiltinSignature {
            full_name: "V6_vgathermhq".to_string(),
            short_name: "vgathermhq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVector,
            ],
        },
    );

    map.insert(
        "vgathermhwq".to_string(),
        BuiltinSignature {
            full_name: "V6_vgathermhwq".to_string(),
            short_name: "vgathermhwq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVectorPair,
            ],
        },
    );

    map.insert(
        "vgathermwq".to_string(),
        BuiltinSignature {
            full_name: "V6_vgathermwq".to_string(),
            short_name: "vgathermwq".to_string(),
            return_type: RustType::Unit,
            param_types: vec![
                RustType::MutPtrHvxVector,
                RustType::HvxVector,
                RustType::I32,
                RustType::I32,
                RustType::HvxVector,
            ],
        },
    );

    // Basic comparison operations (without accumulation)
    for elem in ["b", "h", "w", "ub", "uh", "uw"] {
        // vgt* - greater than
        map.insert(
            format!("vgt{}", elem),
            BuiltinSignature {
                full_name: format!("V6_vgt{}", elem),
                short_name: format!("vgt{}", elem),
                return_type: RustType::HvxVector,
                param_types: vec![RustType::HvxVector, RustType::HvxVector],
            },
        );
        // veq* - equal
        map.insert(
            format!("veq{}", elem),
            BuiltinSignature {
                full_name: format!("V6_veq{}", elem),
                short_name: format!("veq{}", elem),
                return_type: RustType::HvxVector,
                param_types: vec![RustType::HvxVector, RustType::HvxVector],
            },
        );
    }

    // Conditional subtraction operations (vsub*q, vsub*nq)
    for elem in ["b", "h", "w"] {
        map.insert(
            format!("vsub{}q", elem),
            BuiltinSignature {
                full_name: format!("V6_vsub{}q", elem),
                short_name: format!("vsub{}q", elem),
                return_type: RustType::HvxVector,
                param_types: vec![
                    RustType::HvxVector,
                    RustType::HvxVector,
                    RustType::HvxVector,
                ],
            },
        );
        map.insert(
            format!("vsub{}nq", elem),
            BuiltinSignature {
                full_name: format!("V6_vsub{}nq", elem),
                short_name: format!("vsub{}nq", elem),
                return_type: RustType::HvxVector,
                param_types: vec![
                    RustType::HvxVector,
                    RustType::HvxVector,
                    RustType::HvxVector,
                ],
            },
        );
    }

    // vmux - vector mux (select based on predicate)
    map.insert(
        "vmux".to_string(),
        BuiltinSignature {
            full_name: "V6_vmux".to_string(),
            short_name: "vmux".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![
                RustType::HvxVector,
                RustType::HvxVector,
                RustType::HvxVector,
            ],
        },
    );

    // vswap - vector swap based on predicate
    map.insert(
        "vswap".to_string(),
        BuiltinSignature {
            full_name: "V6_vswap".to_string(),
            short_name: "vswap".to_string(),
            return_type: RustType::HvxVectorPair,
            param_types: vec![
                RustType::HvxVector,
                RustType::HvxVector,
                RustType::HvxVector,
            ],
        },
    );

    // shuffeq operations - take vectors (internal pred representation) and return vector
    for elem in ["h", "w"] {
        map.insert(
            format!("shuffeq{}", elem),
            BuiltinSignature {
                full_name: format!("V6_shuffeq{}", elem),
                short_name: format!("shuffeq{}", elem),
                return_type: RustType::HvxVector,
                param_types: vec![RustType::HvxVector, RustType::HvxVector],
            },
        );
    }

    // Predicate AND with vector operations
    map.insert(
        "vandvqv".to_string(),
        BuiltinSignature {
            full_name: "V6_vandvqv".to_string(),
            short_name: "vandvqv".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    map.insert(
        "vandvnqv".to_string(),
        BuiltinSignature {
            full_name: "V6_vandvnqv".to_string(),
            short_name: "vandvnqv".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector],
        },
    );

    // vandnqrt and vandnqrt_acc
    map.insert(
        "vandnqrt".to_string(),
        BuiltinSignature {
            full_name: "V6_vandnqrt".to_string(),
            short_name: "vandnqrt".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::I32],
        },
    );

    map.insert(
        "vandnqrt_acc".to_string(),
        BuiltinSignature {
            full_name: "V6_vandnqrt_acc".to_string(),
            short_name: "vandnqrt_acc".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32],
        },
    );

    // pred_scalar2v2
    map.insert(
        "pred_scalar2v2".to_string(),
        BuiltinSignature {
            full_name: "V6_pred_scalar2v2".to_string(),
            short_name: "pred_scalar2v2".to_string(),
            return_type: RustType::HvxVector,
            param_types: vec![RustType::I32],
        },
    );

    map
}

/// Generate extern declarations for all intrinsics for a specific vector mode
fn generate_extern_block(intrinsics: &[IntrinsicInfo], mode: VectorMode) -> String {
    let mut output = String::new();

    // Collect unique builtins to avoid duplicates
    let mut seen_builtins: HashSet<String> = HashSet::new();
    let mut decls: Vec<(String, String, RustType, Vec<RustType>)> = Vec::new();

    // First, add simple intrinsics
    for info in intrinsics.iter().filter(|i| !i.is_compound) {
        if seen_builtins.contains(&info.builtin_name) {
            continue;
        }
        seen_builtins.insert(info.builtin_name.clone());

        let param_types: Vec<RustType> = info.params.iter().map(|(_, t)| t.clone()).collect();
        decls.push((
            info.builtin_name.clone(),
            info.instr_name.clone(),
            info.return_type.clone(),
            param_types,
        ));
    }

    // Then, collect all builtins used in compound expressions
    let helper_sigs = get_compound_helper_signatures();
    let mut compound_builtins: HashSet<String> = HashSet::new();

    for info in intrinsics.iter().filter(|i| i.is_compound) {
        if let Some(ref expr) = info.compound_expr {
            collect_builtins_from_expr(expr, &mut compound_builtins);
        }
    }

    // Add compound helper builtins
    let mut missing_builtins = Vec::new();
    for builtin_name in compound_builtins {
        let full_name = format!("V6_{}", builtin_name);
        if seen_builtins.contains(&full_name) {
            continue;
        }
        seen_builtins.insert(full_name.clone());

        if let Some(sig) = helper_sigs.get(&builtin_name) {
            decls.push((
                sig.full_name.clone(),
                sig.short_name.clone(),
                sig.return_type.clone(),
                sig.param_types.clone(),
            ));
        } else {
            missing_builtins.push(builtin_name);
        }
    }

    // Report missing builtins (for development purposes)
    if !missing_builtins.is_empty() {
        eprintln!("Warning: Missing helper signatures for compound builtins:");
        for name in &missing_builtins {
            eprintln!("  - {}", name);
        }
    }

    // Sort by builtin name for consistent output
    decls.sort_by(|a, b| a.0.cmp(&b.0));

    // Generate intrinsic declarations for the specified mode
    output.push_str(&format!(
        "// LLVM intrinsic declarations for {}-byte vector mode\n",
        mode.bytes()
    ));
    output.push_str("#[allow(improper_ctypes)]\n");
    output.push_str("unsafe extern \"unadjusted\" {\n");

    for (builtin_name, instr_name, return_type, param_types) in &decls {
        let base_link = builtin_name.replace('_', ".");
        // 128-byte mode uses .128B suffix, 64-byte mode doesn't
        let link_name = if builtin_name.starts_with("V6_") && mode == VectorMode::V128 {
            format!("llvm.hexagon.{}.128B", base_link)
        } else {
            format!("llvm.hexagon.{}", base_link)
        };

        let params_str = if param_types.is_empty() {
            String::new()
        } else {
            param_types
                .iter()
                .map(|t| format!("_: {}", t.to_extern_str()))
                .collect::<Vec<_>>()
                .join(", ")
        };

        let return_str = if *return_type == RustType::Unit {
            " -> ()".to_string()
        } else {
            format!(" -> {}", return_type.to_extern_str())
        };

        output.push_str(&format!(
            "    #[link_name = \"{}\"]\n    fn {}({}){};\n",
            link_name, instr_name, params_str, return_str
        ));
    }

    output.push_str("}\n");
    output
}

/// Generate Rust code for a compound expression
/// `params` maps parameter names to their types in the function signature
/// Get the type of an expression
fn get_expr_type(
    expr: &CompoundExpr,
    params: &HashMap<String, RustType>,
    helper_sigs: &HashMap<String, BuiltinSignature>,
) -> Option<RustType> {
    match expr {
        CompoundExpr::BuiltinCall(name, _) => {
            helper_sigs.get(name).map(|sig| sig.return_type.clone())
        }
        CompoundExpr::Param(name) => params.get(name).cloned(),
        CompoundExpr::IntLiteral(_) => Some(RustType::I32),
    }
}

fn generate_compound_expr_code(
    expr: &CompoundExpr,
    params: &HashMap<String, RustType>,
    helper_sigs: &HashMap<String, BuiltinSignature>,
) -> String {
    match expr {
        CompoundExpr::BuiltinCall(name, args) => {
            // Get the expected parameter types for this builtin
            let expected_types = helper_sigs
                .get(name)
                .map(|sig| sig.param_types.clone())
                .unwrap_or_default();

            let args_code: Vec<String> = args
                .iter()
                .enumerate()
                .map(|(i, arg)| {
                    let arg_code = generate_compound_expr_code(arg, params, helper_sigs);

                    // Check if we need to transmute this argument
                    let expected_type = expected_types.get(i);
                    let actual_type = get_expr_type(arg, params, helper_sigs);

                    // If the builtin expects HvxVector but the arg is HvxVectorPred, transmute
                    if expected_type == Some(&RustType::HvxVector)
                        && actual_type == Some(RustType::HvxVectorPred)
                    {
                        format!(
                            "core::mem::transmute::<HvxVectorPred, HvxVector>({})",
                            arg_code
                        )
                    } else {
                        arg_code
                    }
                })
                .collect();
            format!("{}({})", name, args_code.join(", "))
        }
        CompoundExpr::Param(name) => name.clone(),
        CompoundExpr::IntLiteral(n) => n.to_string(),
    }
}

/// Get the primary instruction name from a compound expression (innermost significant op)
fn get_compound_primary_instr(expr: &CompoundExpr) -> Option<String> {
    match expr {
        CompoundExpr::BuiltinCall(name, args) => {
            // For vandqrt wrapper, look inside
            if name == "vandqrt" && !args.is_empty() {
                if let Some(inner) = get_compound_primary_instr(&args[0]) {
                    return Some(inner);
                }
            }
            // For store operations, use the store name
            if name.starts_with("vS32b") {
                return Some(name.clone());
            }
            // For conditional accumulation, use the add name
            if name.starts_with("vadd") && (name.ends_with("q") || name.ends_with("nq")) {
                return Some(name.clone());
            }
            // For predicate operations
            if name.starts_with("pred_") {
                return Some(name.clone());
            }
            // For comparison operations with accumulation
            if (name.starts_with("veq") || name.starts_with("vgt"))
                && (name.ends_with("_and") || name.ends_with("_or") || name.ends_with("_xor"))
            {
                return Some(name.clone());
            }
            Some(name.clone())
        }
        _ => None,
    }
}

/// Get override implementations for specific compound intrinsics.
/// Some C macros rely on implicit type conversions that don't work with
/// our stricter Rust types, so we provide corrected implementations.
fn get_compound_overrides() -> HashMap<&'static str, &'static str> {
    let mut map = HashMap::new();

    // Q6_V_vand_QR: takes pred, returns vec
    // Use transmute to convert pred to vec for LLVM, call vandvrt
    map.insert(
        "Q6_V_vand_QR",
        "vandvrt(core::mem::transmute::<HvxVectorPred, HvxVector>(qu), rt)",
    );

    // Q6_V_vandor_VQR: takes vec and pred, returns vec
    map.insert(
        "Q6_V_vandor_VQR",
        "vandvrt_acc(vx, core::mem::transmute::<HvxVectorPred, HvxVector>(qu), rt)",
    );

    // Q6_Q_vand_VR: takes vec, returns pred
    map.insert(
        "Q6_Q_vand_VR",
        "core::mem::transmute::<HvxVector, HvxVectorPred>(vandqrt(vu, rt))",
    );

    // Q6_Q_vandor_QVR: takes pred and vec, returns pred
    map.insert(
        "Q6_Q_vandor_QVR",
        "core::mem::transmute::<HvxVector, HvxVectorPred>(vandqrt_acc(core::mem::transmute::<HvxVectorPred, HvxVector>(qx), vu, rt))",
    );

    map
}

/// Generate wrapper functions for all intrinsics
fn generate_functions(intrinsics: &[IntrinsicInfo]) -> String {
    let mut output = String::new();
    let simd_mappings = get_simd_intrinsic_mappings();

    // Generate simple intrinsics
    for info in intrinsics.iter().filter(|i| !i.is_compound) {
        let rust_name = &info.q6_name;

        // Generate doc comment
        output.push_str(&format!("/// `{}`\n", info.asm_syntax));
        output.push_str("///\n");
        output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type));
        output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots));

        // Generate attributes
        output.push_str("#[inline]\n");
        output.push_str(&format!(
            "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n",
            info.min_arch
        ));

        // Check if we should use simd intrinsic instead
        let use_simd = simd_mappings.get(info.instr_name.as_str());

        // assert_instr uses the original instruction name
        output.push_str(&format!(
            "#[cfg_attr(test, assert_instr({}))]\n",
            info.instr_name
        ));

        output.push_str(&format!(
            "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n",
            TRACKING_ISSUE
        ));

        // Generate function signature
        let params_str = info
            .params
            .iter()
            .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str()))
            .collect::<Vec<_>>()
            .join(", ");

        let return_str = if info.return_type == RustType::Unit {
            String::new()
        } else {
            format!(" -> {}", info.return_type.to_rust_str())
        };

        output.push_str(&format!(
            "pub unsafe fn {}({}){} {{\n",
            rust_name, params_str, return_str
        ));

        // Generate function body
        let args_str = info
            .params
            .iter()
            .map(|(name, _)| name.as_str())
            .collect::<Vec<_>>()
            .join(", ");

        if let Some(simd_fn) = use_simd {
            // Use architecture-independent simd intrinsic
            output.push_str(&format!("    {}({})\n", simd_fn, args_str));
        } else {
            // Use the LLVM intrinsic
            output.push_str(&format!("    {}({})\n", info.instr_name, args_str));
        }

        output.push_str("}\n\n");
    }

    // Generate compound intrinsics
    let helper_sigs = get_compound_helper_signatures();
    let overrides = get_compound_overrides();
    for info in intrinsics.iter().filter(|i| i.is_compound) {
        if let Some(ref compound_expr) = info.compound_expr {
            let rust_name = &info.q6_name;

            // Get the primary instruction for assert_instr
            let _primary_instr = get_compound_primary_instr(compound_expr)
                .unwrap_or_else(|| info.instr_name.clone());

            // Generate doc comment
            output.push_str(&format!("/// `{}`\n", info.asm_syntax));
            output.push_str("///\n");
            output.push_str(
                "/// This is a compound operation composed of multiple HVX instructions.\n",
            );
            if !info.instr_type.is_empty() {
                output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type));
            }
            if !info.exec_slots.is_empty() {
                output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots));
            }

            // Generate attributes
            output.push_str("#[inline]\n");
            output.push_str(&format!(
                "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n",
                info.min_arch
            ));

            // For compound ops, we skip assert_instr since they emit multiple instructions
            // output.push_str(&format!(
            //     "#[cfg_attr(test, assert_instr({}))]\n",
            //     primary_instr
            // ));

            output.push_str(&format!(
                "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n",
                TRACKING_ISSUE
            ));

            // Generate function signature
            let params_str = info
                .params
                .iter()
                .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str()))
                .collect::<Vec<_>>()
                .join(", ");

            let return_str = if info.return_type == RustType::Unit {
                String::new()
            } else {
                format!(" -> {}", info.return_type.to_rust_str())
            };

            output.push_str(&format!(
                "pub unsafe fn {}({}){} {{\n",
                rust_name, params_str, return_str
            ));

            // Check if we have an override for this intrinsic
            let body = if let Some(override_body) = overrides.get(info.q6_name.as_str()) {
                override_body.to_string()
            } else {
                // Build param type map for expression code generation
                let param_types: HashMap<String, RustType> = info.params.iter().cloned().collect();
                // Generate function body from compound expression
                let expr_body =
                    generate_compound_expr_code(compound_expr, &param_types, &helper_sigs);

                // Check if we need to transmute the result
                let expr_return_type = get_expr_type(compound_expr, &param_types, &helper_sigs);
                if info.return_type == RustType::HvxVectorPred
                    && expr_return_type == Some(RustType::HvxVector)
                {
                    format!(
                        "core::mem::transmute::<HvxVector, HvxVectorPred>({})",
                        expr_body
                    )
                } else {
                    expr_body
                }
            };
            output.push_str(&format!("    {}\n", body));

            output.push_str("}\n\n");
        }
    }

    output
}

/// Generate a module file for a specific vector mode
fn generate_module_file(
    intrinsics: &[IntrinsicInfo],
    output_path: &Path,
    mode: VectorMode,
) -> Result<(), String> {
    let mut output =
        File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?;

    writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?;
    writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?;
    writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?;
    writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?;

    // Ensure file is flushed before running rustfmt
    drop(output);

    // Run rustfmt on the generated file
    let status = std::process::Command::new("rustfmt")
        .arg(output_path)
        .status()
        .map_err(|e| format!("Failed to run rustfmt: {}", e))?;

    if !status.success() {
        return Err("rustfmt failed".to_string());
    }

    Ok(())
}

fn main() -> Result<(), String> {
    println!("=== Hexagon HVX Code Generator ===\n");

    // Get the crate directory first (needed for both reading header and writing output)
    let crate_dir = std::env::var("CARGO_MANIFEST_DIR")
        .map(std::path::PathBuf::from)
        .unwrap_or_else(|_| std::env::current_dir().unwrap());

    // Read and parse the local LLVM header
    println!("Step 1: Reading LLVM HVX header...");
    let header_content = read_header(&crate_dir)?;
    println!("  Read {} bytes", header_content.len());

    println!("\nStep 2: Parsing intrinsic definitions...");
    let all_intrinsics = parse_header(&header_content);
    println!("  Found {} intrinsic definitions", all_intrinsics.len());

    // Filter out intrinsics requiring architecture versions not yet supported by rustc
    let intrinsics: Vec<_> = all_intrinsics
        .into_iter()
        .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH)
        .collect();
    let filtered_count = intrinsics.len();
    println!(
        "  Filtered to {} intrinsics (max supported: hvxv{})",
        filtered_count, MAX_SUPPORTED_ARCH
    );

    // Count simple vs compound
    let simple_count = intrinsics.iter().filter(|i| !i.is_compound).count();
    let compound_count = intrinsics.iter().filter(|i| i.is_compound).count();
    println!("  Simple intrinsics: {}", simple_count);
    println!("  Compound intrinsics: {}", compound_count);

    // Print some sample intrinsics for verification
    println!("\n  Sample simple intrinsics:");
    for info in intrinsics.iter().filter(|i| !i.is_compound).take(5) {
        println!(
            "    {} -> {} ({})",
            info.q6_name, info.builtin_name, info.asm_syntax
        );
    }

    println!("\n  Sample compound intrinsics:");
    for info in intrinsics.iter().filter(|i| i.is_compound).take(5) {
        println!("    {} ({})", info.q6_name, info.asm_syntax);
    }

    // Count architecture versions
    let mut arch_counts: HashMap<u32, usize> = HashMap::new();
    for info in &intrinsics {
        *arch_counts.entry(info.min_arch).or_insert(0) += 1;
    }
    println!("\n  By architecture version:");
    let mut archs: Vec<_> = arch_counts.iter().collect();
    archs.sort_by_key(|(k, _)| *k);
    for (arch, count) in archs {
        println!("    HVX v{}: {} intrinsics", arch, count);
    }

    // Generate output files
    let hexagon_dir = crate_dir.join("../core_arch/src/hexagon");

    // Generate v64.rs (64-byte vector mode)
    let v64_path = hexagon_dir.join("v64.rs");
    println!("\nStep 3: Generating v64.rs (64-byte mode)...");
    generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?;
    println!("  Output: {}", v64_path.display());

    // Generate v128.rs (128-byte vector mode)
    let v128_path = hexagon_dir.join("v128.rs");
    println!("\nStep 4: Generating v128.rs (128-byte mode)...");
    generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?;
    println!("  Output: {}", v128_path.display());

    println!("\n=== Results ===");
    println!(
        "  Generated {} simple wrapper functions per module",
        simple_count
    );
    println!(
        "  Generated {} compound wrapper functions per module",
        compound_count
    );
    println!(
        "  Total: {} functions per module",
        simple_count + compound_count
    );
    println!("  Output files: v64.rs, v128.rs");

    Ok(())
}
