diff --git a/Cargo.lock b/Cargo.lock index c8cfc21a5a..eadad08880 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -849,13 +849,6 @@ dependencies = [ "stdarch-gen-common", ] -[[package]] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -dependencies = [ - "regex", -] - [[package]] name = "stdarch-gen-loongarch" version = "0.1.0" diff --git a/crates/core_arch/src/hexagon/scalar.rs b/crates/core_arch/src/hexagon/scalar.rs index 477414de74..a42c5c5e10 100644 --- a/crates/core_arch/src/hexagon/scalar.rs +++ b/crates/core_arch/src/hexagon/scalar.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon scalar intrinsics //! //! This module provides intrinsics for scalar (non-HVX) Hexagon DSP operations, diff --git a/crates/stdarch-gen-hexagon-scalar/Cargo.toml b/crates/stdarch-gen-hexagon-scalar/Cargo.toml deleted file mode 100644 index 04bee944f4..0000000000 --- a/crates/stdarch-gen-hexagon-scalar/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -authors = ["The Rust Project Developers"] -license = "MIT OR Apache-2.0" -edition = "2021" - -[dependencies] -regex = "1.10" diff --git a/crates/stdarch-gen-hexagon-scalar/hexagon_protos.h b/crates/stdarch-gen-hexagon/hexagon_protos.h similarity index 100% rename from crates/stdarch-gen-hexagon-scalar/hexagon_protos.h rename to crates/stdarch-gen-hexagon/hexagon_protos.h diff --git a/crates/stdarch-gen-hexagon/src/hvx.rs b/crates/stdarch-gen-hexagon/src/hvx.rs new file mode 100644 index 0000000000..4d4b03c3ac --- /dev/null +++ b/crates/stdarch-gen-hexagon/src/hvx.rs @@ -0,0 +1,1648 @@ +//! 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; +use stdarch_gen_common::GENERATED_MARKER; + +/// 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, +} + +/// Expression tree for compound intrinsics +#[derive(Debug, Clone)] +enum CompoundExpr { + /// A call to a builtin: (builtin_name without V6_ prefix, arguments) + BuiltinCall(String, Vec), + /// 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 { + 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 { + let expr = expr.trim(); + + // Try to match an integer literal (like -1) + if let Ok(n) = expr.parse::() { + 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> { + 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) { + 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 { + 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 { + 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(¯o_body) + .or_else(|| simple_builtin_re2.captures(¯o_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(¯o_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, +} + +/// 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 { + 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 = HashSet::new(); + let mut decls: Vec<(String, String, RustType, Vec)> = 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 = 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 = 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::>() + .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, + helper_sigs: &HashMap, +) -> Option { + 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, + helper_sigs: &HashMap, +) -> 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 = 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::({})", + 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 { + 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::(qu), rt)", + ); + + // Q6_V_vandor_VQR: takes vec and pred, returns vec + map.insert( + "Q6_V_vandor_VQR", + "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", + ); + + // Q6_Q_vand_VR: takes vec, returns pred + map.insert( + "Q6_Q_vand_VR", + "core::mem::transmute::(vandqrt(vu, rt))", + ); + + // Q6_Q_vandor_QVR: takes pred and vec, returns pred + map.insert( + "Q6_Q_vandor_QVR", + "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(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::>() + .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::>() + .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::>() + .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 = info.params.iter().cloned().collect(); + // Generate function body from compound expression + let expr_body = + generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); + + // Check if we need to transmute the result + let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); + if info.return_type == RustType::HvxVectorPred + && expr_return_type == Some(RustType::HvxVector) + { + format!( + "core::mem::transmute::({})", + 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, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; + 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(()) +} + +/// Parse the HVX header in `crate_dir` and write `v64.rs` and `v128.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; + let all_intrinsics = parse_header(&header_content); + let intrinsics: Vec<_> = all_intrinsics + .into_iter() + .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH) + .collect(); + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + } + Ok(()) +} diff --git a/crates/stdarch-gen-hexagon/src/main.rs b/crates/stdarch-gen-hexagon/src/main.rs index c3ad153ab0..9eab85a46e 100644 --- a/crates/stdarch-gen-hexagon/src/main.rs +++ b/crates/stdarch-gen-hexagon/src/main.rs @@ -1,1728 +1,31 @@ -//! Hexagon HVX Code Generator +//! Hexagon 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. +//! Single binary that produces every generated file under +//! `core_arch/src/hexagon/`: scalar.rs (scalar intrinsics) and +//! v64.rs / v128.rs (HVX intrinsics). //! -//! 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; -use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; - -/// 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, -} - -/// Expression tree for compound intrinsics -#[derive(Debug, Clone)] -enum CompoundExpr { - /// A call to a builtin: (builtin_name without V6_ prefix, arguments) - BuiltinCall(String, Vec), - /// 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 { - 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 { - let expr = expr.trim(); - - // Try to match an integer literal (like -1) - if let Ok(n) = expr.parse::() { - 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> { - 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) { - 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 { - 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 { - 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(¯o_body) - .or_else(|| simple_builtin_re2.captures(¯o_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(¯o_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, -} - -/// 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 { - 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], - }, - ); +//! Run in check or bless mode via `STDARCH_GEN_MODE`. - // 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], - }, - ); +mod hvx; +mod scalar; - // 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 = HashSet::new(); - let mut decls: Vec<(String, String, RustType, Vec)> = 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 = 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 = 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::>() - .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, - helper_sigs: &HashMap, -) -> Option { - 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, - helper_sigs: &HashMap, -) -> 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 = 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::({})", - 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 { - 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::(qu), rt)", - ); - - // Q6_V_vandor_VQR: takes vec and pred, returns vec - map.insert( - "Q6_V_vandor_VQR", - "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", - ); - - // Q6_Q_vand_VR: takes vec, returns pred - map.insert( - "Q6_Q_vand_VR", - "core::mem::transmute::(vandqrt(vu, rt))", - ); - - // Q6_Q_vandor_QVR: takes pred and vec, returns pred - map.insert( - "Q6_Q_vandor_QVR", - "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(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::>() - .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::>() - .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::>() - .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 = info.params.iter().cloned().collect(); - // Generate function body from compound expression - let expr_body = - generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); - - // Check if we need to transmute the result - let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); - if info.return_type == RustType::HvxVectorPred - && expr_return_type == Some(RustType::HvxVector) - { - format!( - "core::mem::transmute::({})", - 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, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; - 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(()) -} +use std::path::PathBuf; +use stdarch_gen_common::{run_generator, Mode}; 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) + .map(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 = 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"); - - // Either "check" to check the output versus the committed output, or "bless" - // to update the output. let mode = Mode::from_env(); - println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { - for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { - let path = out_dir.join(filename); - generate_module_file(&intrinsics, &path, vmode)?; - println!(" Output: {}", hexagon_dir.join(filename).display()); - } + scalar::generate(&crate_dir, out_dir)?; + hvx::generate(&crate_dir, out_dir)?; Ok(()) }) .map_err(|e| e.to_string())?; - 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(()) } diff --git a/crates/stdarch-gen-hexagon-scalar/src/main.rs b/crates/stdarch-gen-hexagon/src/scalar.rs similarity index 96% rename from crates/stdarch-gen-hexagon-scalar/src/main.rs rename to crates/stdarch-gen-hexagon/src/scalar.rs index 934c7e28fe..4beee35df9 100644 --- a/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/crates/stdarch-gen-hexagon/src/scalar.rs @@ -21,6 +21,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::GENERATED_MARKER; /// Extract the instruction mnemonic from the assembly syntax string. /// @@ -630,6 +631,7 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc()).map_err(|e| e.to_string())?; writeln!(output, "").map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics)).map_err(|e| e.to_string())?; @@ -651,28 +653,12 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R Ok(()) } -fn main() -> Result<(), String> { - println!("=== Hexagon Scalar Code Generator ===\n"); - - let crate_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) - .unwrap_or_else(|_| std::env::current_dir().unwrap()); - - let header_content = read_header(&crate_dir)?; - println!("Read {} bytes", header_content.len()); - +/// Parse the scalar header in `crate_dir` and write `scalar.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; let intrinsics = parse_header(&header_content); - println!("Parsed {} scalar intrinsics", intrinsics.len()); - - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - let scalar_path = hexagon_dir.join("scalar.rs"); - + std::fs::create_dir_all(out_dir).map_err(|e| e.to_string())?; + let scalar_path = out_dir.join("scalar.rs"); generate_scalar_file(&intrinsics, &scalar_path)?; - println!("Generated scalar.rs at {}", scalar_path.display()); - Ok(()) }