use std::env; use std::fmt; use std::fs::File; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::PathBuf; /// Complete lines of generated source. /// /// This enables common generation tasks to be factored out without precluding basic /// context-specific formatting. /// /// The convention in this generator is to prefix (not suffix) lines with a newline, so the /// implementation of `std::fmt::Display` behaves in the same way. struct Lines { indent: usize, lines: Vec, } impl Lines { fn single(line: String) -> Self { Self::from(vec![line]) } } impl From> for Lines { fn from(lines: Vec) -> Self { Self { indent: 0, lines } } } impl std::fmt::Display for Lines { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { for line in self.lines.iter() { write!(f, "\n{:width$}{line}", "", width = self.indent)?; } Ok(()) } } #[derive(Clone, Copy, PartialEq)] enum TargetFeature { Lsx, Lasx, } impl TargetFeature { fn new(ext: &str) -> TargetFeature { match ext { "lasx" => Self::Lasx, _ => Self::Lsx, } } /// A string for use with `#[target_feature(...)]`. fn as_target_feature_arg(&self, ins: &str) -> String { let vec = match *self { // Features included with LoongArch64 LSX and LASX. Self::Lsx => "lsx", Self::Lasx => "lasx", }; let frecipe = match ins { "lsx_vfrecipe_s" | "lsx_vfrecipe_d" | "lsx_vfrsqrte_s" | "lsx_vfrsqrte_d" | "lasx_xvfrecipe_s" | "lasx_xvfrecipe_d" | "lasx_xvfrsqrte_s" | "lasx_xvfrsqrte_d" => { ",frecipe" } _ => "", }; format!("{vec}{frecipe}") } fn attr(name: &str, value: impl fmt::Display) -> String { format!(r#"#[{name}(enable = "{value}")]"#) } /// Generate a target_feature attribute fn to_target_feature_attr(self, ins: &str) -> Lines { Lines::single(Self::attr( "target_feature", self.as_target_feature_arg(ins), )) } fn bytes(&self) -> u8 { match *self { // Features included with LoongArch64 LSX and LASX. Self::Lsx => 16, Self::Lasx => 32, } } } fn gen_spec(in_file: String, ext_name: &str) -> io::Result<()> { let f = File::open(in_file.clone()).unwrap_or_else(|_| panic!("Failed to open {in_file}")); let f = BufReader::new(f); let mut out = format!( r#"// This code is automatically generated. DO NOT MODIFY. // ``` // OUT_DIR=`pwd`/crates/stdarch-gen-loongarch cargo run -p stdarch-gen-loongarch -- {in_file} // ``` "# ); out.push('\n'); let mut asm_fmts = String::new(); let mut data_types = String::new(); let fn_pat = format!("__{ext_name}_"); for line in f.lines() { let line = line.unwrap(); if line.is_empty() { continue; } if let Some(s) = line.find("/* Assembly instruction format:") { let e = line.find('.').unwrap(); asm_fmts = line.get(s + 31..e).unwrap().trim().to_string(); } else if let Some(s) = line.find("/* Data types in instruction templates:") { let e = line.find('.').unwrap(); data_types = line.get(s + 39..e).unwrap().trim().to_string(); } else if let Some(s) = line.find(fn_pat.as_str()) { let e = line.find('(').unwrap(); let name = line.get(s + 2..e).unwrap().trim().to_string(); out.push_str(&format!("/// {name}\n")); out.push_str(&format!("name = {name}\n")); out.push_str(&format!("asm-fmts = {asm_fmts}\n")); out.push_str(&format!("data-types = {data_types}\n")); out.push('\n'); } } let out_dir_path: PathBuf = PathBuf::from(env::var("OUT_DIR").unwrap()); std::fs::create_dir_all(&out_dir_path)?; let mut f = File::create(out_dir_path.join(format!("{ext_name}.spec")))?; f.write_all(out.as_bytes())?; Ok(()) } fn gen_bind(in_file: String, ext_name: &str) -> io::Result<()> { let f = File::open(in_file.clone()).unwrap_or_else(|_| panic!("Failed to open {in_file}")); let f = BufReader::new(f); let target: TargetFeature = TargetFeature::new(ext_name); let mut para_num; let mut current_name: Option = None; let mut asm_fmts: Vec = Vec::new(); let mut link_function_str = String::new(); let mut function_str = String::new(); let mut out = String::new(); out.push_str(&format!( r#"// This code is automatically generated. DO NOT MODIFY. // // Instead, modify `{in_file}` and run the following command to re-generate this file: // // ``` // OUT_DIR=`pwd`/crates/core_arch cargo run -p stdarch-gen-loongarch -- {in_file} // ``` use crate::mem::transmute; use super::types::*; "# )); out.push_str( r#" #[allow(improper_ctypes)] unsafe extern "unadjusted" { "#, ); for line in f.lines() { let line = line.unwrap(); if line.is_empty() { continue; } if let Some(name) = line.strip_prefix("name = ") { current_name = Some(String::from(name)); } else if line.starts_with("asm-fmts = ") { asm_fmts = line[10..] .split(',') .map(|v| v.trim().to_string()) .collect(); } else if line.starts_with("data-types = ") { let current_name = current_name.clone().unwrap(); let data_types: Vec<&str> = line .get(12..) .unwrap() .split(',') .map(|e| e.trim()) .collect(); let in_t; let out_t; if data_types.len() == 2 { in_t = [data_types[1], "NULL", "NULL", "NULL"]; out_t = data_types[0]; para_num = 1; } else if data_types.len() == 3 { in_t = [data_types[1], data_types[2], "NULL", "NULL"]; out_t = data_types[0]; para_num = 2; } else if data_types.len() == 4 { in_t = [data_types[1], data_types[2], data_types[3], "NULL"]; out_t = data_types[0]; para_num = 3; } else if data_types.len() == 5 { in_t = [data_types[1], data_types[2], data_types[3], data_types[4]]; out_t = data_types[0]; para_num = 4; } else { panic!("DEBUG: line: {0} len: {1}", line, data_types.len()); } let (link_function, function) = gen_bind_body(¤t_name, &asm_fmts, &in_t, out_t, para_num, target); link_function_str.push_str(&link_function); function_str.push_str(&function); } } out.push_str(&link_function_str); out.push_str("}\n"); out.push_str(&function_str); let out_path: PathBuf = PathBuf::from(env::var("OUT_DIR").unwrap_or("crates/core_arch".to_string())) .join("src") .join("loongarch64") .join(ext_name); std::fs::create_dir_all(&out_path)?; let mut file = File::create(out_path.join("generated.rs"))?; file.write_all(out.as_bytes())?; Ok(()) } fn gen_bind_body( current_name: &str, asm_fmts: &[String], in_t: &[&str; 4], out_t: &str, para_num: i32, target: TargetFeature, ) -> (String, String) { enum TypeKind { Vector, Intrinsic, } use TypeKind::*; let type_to_rst = |t: &str, s: bool, k: TypeKind| -> &str { match (t, s, k) { ("V16QI", _, Vector) => "__v16i8", ("V16QI", _, Intrinsic) => "m128i", ("V32QI", _, Vector) => "__v32i8", ("V32QI", _, Intrinsic) => "m256i", ("V8HI", _, Vector) => "__v8i16", ("V8HI", _, Intrinsic) => "m128i", ("V16HI", _, Vector) => "__v16i16", ("V16HI", _, Intrinsic) => "m256i", ("V4SI", _, Vector) => "__v4i32", ("V4SI", _, Intrinsic) => "m128i", ("V8SI", _, Vector) => "__v8i32", ("V8SI", _, Intrinsic) => "m256i", ("V2DI", _, Vector) => "__v2i64", ("V2DI", _, Intrinsic) => "m128i", ("V4DI", _, Vector) => "__v4i64", ("V4DI", _, Intrinsic) => "m256i", ("UV16QI", _, Vector) => "__v16u8", ("UV16QI", _, Intrinsic) => "m128i", ("UV32QI", _, Vector) => "__v32u8", ("UV32QI", _, Intrinsic) => "m256i", ("UV8HI", _, Vector) => "__v8u16", ("UV8HI", _, Intrinsic) => "m128i", ("UV16HI", _, Vector) => "__v16u16", ("UV16HI", _, Intrinsic) => "m256i", ("UV4SI", _, Vector) => "__v4u32", ("UV4SI", _, Intrinsic) => "m128i", ("UV8SI", _, Vector) => "__v8u32", ("UV8SI", _, Intrinsic) => "m256i", ("UV2DI", _, Vector) => "__v2u64", ("UV2DI", _, Intrinsic) => "m128i", ("UV4DI", _, Vector) => "__v4u64", ("UV4DI", _, Intrinsic) => "m256i", ("SI", _, _) => "i32", ("DI", _, _) => "i64", ("USI", _, _) => "u32", ("UDI", _, _) => "u64", ("V4SF", _, Vector) => "__v4f32", ("V4SF", _, Intrinsic) => "m128", ("V8SF", _, Vector) => "__v8f32", ("V8SF", _, Intrinsic) => "m256", ("V2DF", _, Vector) => "__v2f64", ("V2DF", _, Intrinsic) => "m128d", ("V4DF", _, Vector) => "__v4f64", ("V4DF", _, Intrinsic) => "m256d", ("UQI", _, _) => "u32", ("QI", _, _) => "i32", ("CVPOINTER", false, _) => "*const i8", ("CVPOINTER", true, _) => "*mut i8", ("HI", _, _) => "i32", (_, _, _) => panic!("unknown type: {t}"), } }; let is_mem = in_t.iter().any(|s| s.contains("POINTER")); let is_store = current_name.to_string().contains("vst"); let link_function = { let fn_decl = { let fn_output = if out_t.to_lowercase() == "void" { String::new() } else { format!(" -> {}", type_to_rst(out_t, is_store, Vector)) }; let fn_inputs = match para_num { 1 => format!("(a: {})", type_to_rst(in_t[0], is_store, Vector)), 2 => format!( "(a: {}, b: {})", type_to_rst(in_t[0], is_store, Vector), type_to_rst(in_t[1], is_store, Vector) ), 3 => format!( "(a: {}, b: {}, c: {})", type_to_rst(in_t[0], is_store, Vector), type_to_rst(in_t[1], is_store, Vector), type_to_rst(in_t[2], is_store, Vector) ), 4 => format!( "(a: {}, b: {}, c: {}, d: {})", type_to_rst(in_t[0], is_store, Vector), type_to_rst(in_t[1], is_store, Vector), type_to_rst(in_t[2], is_store, Vector), type_to_rst(in_t[3], is_store, Vector) ), _ => panic!("unsupported parameter number"), }; format!("fn __{current_name}{fn_inputs}{fn_output};") }; let function = format!( r#" #[link_name = "llvm.loongarch.{}"] {fn_decl} "#, current_name.replace('_', ".") ); function }; let type_to_imm = |t| -> i8 { match t { 'b' => 4, 'h' => 3, 'w' => 2, 'd' => 1, _ => panic!("unsupported type"), } }; let mut rustc_legacy_const_generics = ""; let fn_decl = { let fn_output = if out_t.to_lowercase() == "void" { String::new() } else { format!("-> {} ", type_to_rst(out_t, is_store, Intrinsic)) }; let mut fn_inputs = match para_num { 1 => format!("(a: {})", type_to_rst(in_t[0], is_store, Intrinsic)), 2 => format!( "(a: {}, b: {})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic) ), 3 => format!( "(a: {}, b: {}, c: {})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic) ), 4 => format!( "(a: {}, b: {}, c: {}, d: {})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic), type_to_rst(in_t[3], is_store, Intrinsic) ), _ => panic!("unsupported parameter number"), }; if para_num == 1 && in_t[0] == "HI" { fn_inputs = match asm_fmts[1].as_str() { "si13" | "i13" => format!( "()", type_to_rst(in_t[0], is_store, Intrinsic) ), "si10" => format!( "()", type_to_rst(in_t[0], is_store, Intrinsic) ), _ => panic!("unsupported assembly format: {}", asm_fmts[1]), }; rustc_legacy_const_generics = "rustc_legacy_const_generics(0)"; } else if para_num == 2 && (in_t[1] == "UQI" || in_t[1] == "USI") { fn_inputs = if asm_fmts[2].starts_with("ui") { format!( "(a: {0})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]); }; rustc_legacy_const_generics = "rustc_legacy_const_generics(1)"; } else if para_num == 2 && in_t[1] == "QI" { fn_inputs = if asm_fmts[2].starts_with("si") { format!( "(a: {0})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]); }; rustc_legacy_const_generics = "rustc_legacy_const_generics(1)"; } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "SI" { fn_inputs = if asm_fmts[2].starts_with("si") { format!( "(mem_addr: {0})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]); }; rustc_legacy_const_generics = "rustc_legacy_const_generics(1)"; } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "DI" { fn_inputs = match asm_fmts[2].as_str() { "rk" => format!( "(mem_addr: {}, b: {})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic) ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 3 && (in_t[2] == "USI" || in_t[2] == "UQI") { fn_inputs = if asm_fmts[2].starts_with("ui") { format!( "(a: {0}, b: {1})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic), asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]) }; rustc_legacy_const_generics = "rustc_legacy_const_generics(2)"; } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "SI" { fn_inputs = match asm_fmts[2].as_str() { "si12" => format!( "(a: {0}, mem_addr: {1})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic) ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; rustc_legacy_const_generics = "rustc_legacy_const_generics(2)"; } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "DI" { fn_inputs = match asm_fmts[2].as_str() { "rk" => format!( "(a: {}, mem_addr: {}, b: {})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic) ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 4 { fn_inputs = match (asm_fmts[2].as_str(), current_name.chars().last().unwrap()) { ("si8", t) => format!( "(a: {0}, mem_addr: {1})", type_to_rst(in_t[0], is_store, Intrinsic), type_to_rst(in_t[1], is_store, Intrinsic), type_to_rst(in_t[2], is_store, Intrinsic), type_to_rst(in_t[3], is_store, Intrinsic), type_to_imm(t), ), (_, _) => panic!( "unsupported assembly format: {} for {}", asm_fmts[2], current_name ), }; rustc_legacy_const_generics = "rustc_legacy_const_generics(2, 3)"; } format!( "pub {}fn {current_name}{fn_inputs} {fn_output}", if is_mem { "unsafe " } else { "" } ) }; let unsafe_start = if !is_mem { "unsafe { " } else { "" }; let unsafe_end = if !is_mem { " }" } else { "" }; let mut call_params = { match para_num { 1 => format!("{unsafe_start}transmute(__{current_name}(transmute(a))){unsafe_end}"), 2 => format!( "{unsafe_start}transmute(__{current_name}(transmute(a), transmute(b))){unsafe_end}" ), 3 => format!( "{unsafe_start}transmute(__{current_name}(transmute(a), transmute(b), transmute(c))){unsafe_end}" ), 4 => format!( "{unsafe_start}transmute(__{current_name}(transmute(a), transmute(b), transmute(c), transmute(d))){unsafe_end}" ), _ => panic!("unsupported parameter number"), } }; if para_num == 1 && in_t[0] == "HI" { call_params = match asm_fmts[1].as_str() { "si10" => { format!( "static_assert_simm_bits!(IMM_S10, 10);\n {unsafe_start}transmute(__{current_name}(IMM_S10)){unsafe_end}" ) } "i13" => { format!( "static_assert_simm_bits!(IMM_S13, 13);\n {unsafe_start}transmute(__{current_name}(IMM_S13)){unsafe_end}" ) } _ => panic!("unsupported assembly format: {}", asm_fmts[2]), } } else if para_num == 2 && (in_t[1] == "UQI" || in_t[1] == "USI") { call_params = if asm_fmts[2].starts_with("ui") { format!( "static_assert_uimm_bits!(IMM{0}, {0});\n {unsafe_start}transmute(__{current_name}(transmute(a), IMM{0})){unsafe_end}", asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]) }; } else if para_num == 2 && in_t[1] == "QI" { call_params = match asm_fmts[2].as_str() { "si5" => { format!( "static_assert_simm_bits!(IMM_S5, 5);\n {unsafe_start}transmute(__{current_name}(transmute(a), IMM_S5)){unsafe_end}" ) } _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "SI" { call_params = if asm_fmts[2].starts_with("si") { format!( "static_assert_simm_bits!(IMM_S{0}, {0});\n {unsafe_start}transmute(__{current_name}(mem_addr, IMM_S{0})){unsafe_end}", asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]) } } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "DI" { call_params = match asm_fmts[2].as_str() { "rk" => format!( "{unsafe_start}transmute(__{current_name}(mem_addr, transmute(b))){unsafe_end}" ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 3 && (in_t[2] == "USI" || in_t[2] == "UQI") { call_params = if asm_fmts[2].starts_with("ui") { format!( "static_assert_uimm_bits!(IMM{0}, {0});\n {unsafe_start}transmute(__{current_name}(transmute(a), transmute(b), IMM{0})){unsafe_end}", asm_fmts[2].get(2..).unwrap() ) } else { panic!("unsupported assembly format: {}", asm_fmts[2]) } } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "SI" { call_params = match asm_fmts[2].as_str() { "si12" => format!( "static_assert_simm_bits!(IMM_S12, 12);\n {unsafe_start}transmute(__{current_name}(transmute(a), mem_addr, IMM_S12)){unsafe_end}" ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "DI" { call_params = match asm_fmts[2].as_str() { "rk" => format!( "{unsafe_start}transmute(__{current_name}(transmute(a), mem_addr, transmute(b))){unsafe_end}" ), _ => panic!("unsupported assembly format: {}", asm_fmts[2]), }; } else if para_num == 4 { call_params = match (asm_fmts[2].as_str(), current_name.chars().last().unwrap()) { ("si8", t) => format!( "static_assert_simm_bits!(IMM_S8, 8);\n static_assert_uimm_bits!(IMM{0}, {0});\n {unsafe_start}transmute(__{current_name}(transmute(a), mem_addr, IMM_S8, IMM{0})){unsafe_end}", type_to_imm(t) ), (_, _) => panic!( "unsupported assembly format: {} for {}", asm_fmts[2], current_name ), } } let function = if !rustc_legacy_const_generics.is_empty() { format!( r#" #[inline]{target_feature} #[{rustc_legacy_const_generics}] #[unstable(feature = "stdarch_loongarch", issue = "117427")] {fn_decl}{{ {call_params} }} "#, target_feature = target.to_target_feature_attr(current_name) ) } else { format!( r#" #[inline]{target_feature} #[unstable(feature = "stdarch_loongarch", issue = "117427")] {fn_decl}{{ {call_params} }} "#, target_feature = target.to_target_feature_attr(current_name) ) }; (link_function, function) } fn gen_test(in_file: String, ext_name: &str) -> io::Result<()> { let f = File::open(in_file.clone()).unwrap_or_else(|_| panic!("Failed to open {in_file}")); let f = BufReader::new(f); let target: TargetFeature = TargetFeature::new(ext_name); let mut para_num; let mut current_name: Option = None; let mut asm_fmts: Vec = Vec::new(); let mut impl_function_str = String::new(); let mut call_function_str = String::new(); let mut out = String::new(); out.push_str(&format!( r#"/* * This code is automatically generated. DO NOT MODIFY. * * Instead, modify `{in_file}` and run the following command to re-generate this file: * * ``` * OUT_DIR=`pwd`/crates/stdarch-gen-loongarch cargo run -p stdarch-gen-loongarch -- {in_file} test * ``` */ #include #include #include #include union v16qi {{ __m128i v; int64_t i64[2]; int8_t i8[16]; }}; union v32qi {{ __m256i v; int64_t i64[4]; int8_t i8[32]; }}; union v8hi {{ __m128i v; int64_t i64[2]; int16_t i16[8]; }}; union v16hi {{ __m256i v; int64_t i64[4]; int16_t i16[16]; }}; union v4si {{ __m128i v; int64_t i64[2]; int32_t i32[4]; }}; union v8si {{ __m256i v; int64_t i64[4]; int32_t i32[8]; }}; union v2di {{ __m128i v; int64_t i64[2]; }}; union v4di {{ __m256i v; int64_t i64[4]; }}; union uv16qi {{ __m128i v; uint64_t i64[2]; uint8_t i8[16]; }}; union uv32qi {{ __m256i v; uint64_t i64[4]; uint8_t i8[32]; }}; union uv8hi {{ __m128i v; uint64_t i64[2]; uint16_t i16[8]; }}; union uv16hi {{ __m256i v; uint64_t i64[4]; uint16_t i16[16]; }}; union uv4si {{ __m128i v; uint64_t i64[2]; uint32_t i32[4]; }}; union uv8si {{ __m256i v; uint64_t i64[4]; uint32_t i32[8]; }}; union uv2di {{ __m128i v; uint64_t i64[2]; }}; union uv4di {{ __m256i v; uint64_t i64[4]; }}; union v4sf {{ __m128 v; int64_t i64[2]; uint32_t i32[2]; float f32[4]; }}; union v8sf {{ __m256 v; int64_t i64[4]; uint32_t i32[4]; float f32[8]; }}; union v2df {{ __m128d v; uint64_t i64[2]; double f64[2]; }}; union v4df {{ __m256d v; uint64_t i64[4]; double f64[4]; }}; "# )); for line in f.lines() { let line = line.unwrap(); if line.is_empty() { continue; } if let Some(name) = line.strip_prefix("name = ") { current_name = Some(String::from(name)); } else if line.starts_with("asm-fmts = ") { asm_fmts = line[10..] .split(',') .map(|v| v.trim().to_string()) .collect(); } else if line.starts_with("data-types = ") { let current_name = current_name.clone().unwrap(); let data_types: Vec<&str> = line .get(12..) .unwrap() .split(',') .map(|e| e.trim()) .collect(); let in_t; let out_t; if data_types.len() == 2 { in_t = [data_types[1], "NULL", "NULL", "NULL"]; out_t = data_types[0]; para_num = 1; } else if data_types.len() == 3 { in_t = [data_types[1], data_types[2], "NULL", "NULL"]; out_t = data_types[0]; para_num = 2; } else if data_types.len() == 4 { in_t = [data_types[1], data_types[2], data_types[3], "NULL"]; out_t = data_types[0]; para_num = 3; } else if data_types.len() == 5 { in_t = [data_types[1], data_types[2], data_types[3], data_types[4]]; out_t = data_types[0]; para_num = 4; } else { panic!("DEBUG: line: {0} len: {1}", line, data_types.len()); } let (link_function, function) = gen_test_body(¤t_name, &asm_fmts, &in_t, out_t, para_num, target); impl_function_str.push_str(&link_function); call_function_str.push_str(&function); } } out.push_str(&impl_function_str); out.push('\n'); out.push_str("int main(int argc, char *argv[])\n"); out.push_str("{\n"); out.push_str(" printf(\"// This code is automatically generated. DO NOT MODIFY.\\n\");\n"); out.push_str(" printf(\"// See crates/stdarch-gen-loongarch/README.md\\n\\n\");\n"); out.push_str(" printf(\"use crate::{\\n\");\n"); out.push_str(" printf(\" core_arch::{loongarch64::*, simd::*},\\n\");\n"); out.push_str(" printf(\" mem::transmute,\\n\");\n"); out.push_str(" printf(\"};\\n\");\n"); out.push_str(" printf(\"use stdarch_test::simd_test;\\n\");\n"); out.push_str(&call_function_str); out.push_str(" return 0;\n"); out.push('}'); let out_dir_path: PathBuf = PathBuf::from(env::var("OUT_DIR").unwrap()); std::fs::create_dir_all(&out_dir_path)?; let mut f = File::create(out_dir_path.join(format!("{ext_name}.c")))?; f.write_all(out.as_bytes())?; Ok(()) } fn gen_test_body( current_name: &str, asm_fmts: &[String], in_t: &[&str; 4], out_t: &str, para_num: i32, target: TargetFeature, ) -> (String, String) { let rand_i32 = |bits: u8| -> i32 { let val = rand::random::(); let bits = 32 - bits; (val << bits) >> bits }; let rand_u32 = |bits: u8| -> u32 { let val = rand::random::(); let bits = 32 - bits; (val << bits) >> bits }; let rand_i64 = || -> i64 { rand::random::() }; let rand_u64 = || -> u64 { rand::random::() }; let rand_f32 = || -> f32 { rand::random::() }; let rand_f64 = || -> f64 { rand::random::() }; let type_to_ct = |t: &str| -> &str { match t { "V16QI" => "union v16qi", "V32QI" => "union v32qi", "V8HI" => "union v8hi", "V16HI" => "union v16hi", "V4SI" => "union v4si", "V8SI" => "union v8si", "V2DI" => "union v2di", "V4DI" => "union v4di", "UV16QI" => "union uv16qi", "UV32QI" => "union uv32qi", "UV8HI" => "union uv8hi", "UV16HI" => "union uv16hi", "UV4SI" => "union uv4si", "UV8SI" => "union uv8si", "UV2DI" => "union uv2di", "UV4DI" => "union uv4di", "SI" => "int32_t", "DI" => "int64_t", "USI" => "uint32_t", "UDI" => "uint64_t", "V4SF" => "union v4sf", "V8SF" => "union v8sf", "V2DF" => "union v2df", "V4DF" => "union v4df", "UQI" => "uint32_t", "QI" => "int32_t", "CVPOINTER" => "void*", "HI" => "int32_t", _ => panic!("unknown type: {t}"), } }; let type_to_va = |v: &str, t: &str| -> String { let n = if v.starts_with('_') { v.get(1..).unwrap() } else { v }; let mut out = String::new(); match t { "A16QI" => { for i in 0..16 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let {n}: [i8; 16] = [%d")); for _ in 1..16 { out.push_str(", %d"); } out.push_str(&format!("];\\n\",\n {v}.i8[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i8[{i}]")); } } "AM16QI" => { for i in 0..16 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let mut {n}: [i8; 16] = [%d")); for _ in 1..16 { out.push_str(", %d"); } out.push_str(&format!("];\\n\",\n {v}.i8[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i8[{i}]")); } } "V16QI" => { for i in 0..16 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let {n} = i8x16::new(%d")); for _ in 1..16 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i8[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i8[{i}]")); } } "V32QI" => { for i in 0..32 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let {n} = i8x32::new(%d")); for _ in 1..32 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i8[0]")); for i in 1..32 { out.push_str(&format!(", {v}.i8[{i}]")); } } "A32QI" => { for i in 0..32 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let {n}: [i8; 32] = [%d")); for _ in 1..32 { out.push_str(", %d"); } out.push_str(&format!("];\\n\",\n {v}.i8[0]")); for i in 1..32 { out.push_str(&format!(", {v}.i8[{i}]")); } } "AM32QI" => { for i in 0..32 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_i32(8))); } out.push_str(&format!(" printf(\" let mut {n}: [i8; 32] = [%d")); for _ in 1..32 { out.push_str(", %d"); } out.push_str(&format!("];\\n\",\n {v}.i8[0]")); for i in 1..32 { out.push_str(&format!(", {v}.i8[{i}]")); } } "V8HI" => { for i in 0..8 { out.push_str(&format!(" {v}.i16[{i}] = {};\n", rand_i32(16))); } out.push_str(&format!(" printf(\" let {n} = i16x8::new(%d")); for _ in 1..8 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i16[0]")); for i in 1..8 { out.push_str(&format!(", {v}.i16[{i}]")); } } "V16HI" => { for i in 0..16 { out.push_str(&format!(" {v}.i16[{i}] = {};\n", rand_i32(16))); } out.push_str(&format!(" printf(\" let {n} = i16x16::new(%d")); for _ in 1..16 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i16[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i16[{i}]")); } } "V4SI" => { for i in 0..4 { out.push_str(&format!(" {v}.i32[{i}] = {};\n", rand_i32(32))); } out.push_str(&format!(" printf(\" let {n} = i32x4::new(%d")); for _ in 1..4 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i32[{i}]")); } } "V8SI" => { for i in 0..8 { out.push_str(&format!(" {v}.i32[{i}] = {};\n", rand_i32(32))); } out.push_str(&format!(" printf(\" let {n} = i32x8::new(%d")); for _ in 1..8 { out.push_str(", %d"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..8 { out.push_str(&format!(", {v}.i32[{i}]")); } } "V2DI" => { for i in 0..2 { out.push_str(&format!(" {v}.i64[{i}] = {}L;\n", rand_i64())); } out.push_str(&format!(" printf(\" let {n} = i64x2::new(%ld")); for _ in 1..2 { out.push_str(", %ld"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..2 { out.push_str(&format!(", {v}.i64[{i}]")); } } "V4DI" => { for i in 0..4 { out.push_str(&format!(" {v}.i64[{i}] = {}L;\n", rand_i64())); } out.push_str(&format!(" printf(\" let {n} = i64x4::new(%ld")); for _ in 1..4 { out.push_str(", %ld"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i64[{i}]")); } } "UV16QI" => { for i in 0..16 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_u32(8))); } out.push_str(&format!(" printf(\" let {n} = u8x16::new(%u")); for _ in 1..16 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i8[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i8[{i}]")); } } "UV32QI" => { for i in 0..32 { out.push_str(&format!(" {v}.i8[{i}] = {};\n", rand_u32(8))); } out.push_str(&format!(" printf(\" let {n} = u8x32::new(%u")); for _ in 1..32 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i8[0]")); for i in 1..32 { out.push_str(&format!(", {v}.i8[{i}]")); } } "UV8HI" => { for i in 0..8 { out.push_str(&format!(" {v}.i16[{i}] = {};\n", rand_u32(16))); } out.push_str(&format!(" printf(\" let {n} = u16x8::new(%u")); for _ in 1..8 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i16[0]")); for i in 1..8 { out.push_str(&format!(", {v}.i16[{i}]")); } } "UV16HI" => { for i in 0..16 { out.push_str(&format!(" {v}.i16[{i}] = {};\n", rand_u32(16))); } out.push_str(&format!(" printf(\" let {n} = u16x16::new(%u")); for _ in 1..16 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i16[0]")); for i in 1..16 { out.push_str(&format!(", {v}.i16[{i}]")); } } "UV4SI" => { for i in 0..4 { out.push_str(&format!(" {v}.i32[{i}] = {};\n", rand_u32(32))); } out.push_str(&format!(" printf(\" let {n} = u32x4::new(%u")); for _ in 1..4 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i32[{i}]")); } } "UV8SI" => { for i in 0..8 { out.push_str(&format!(" {v}.i32[{i}] = {};\n", rand_u32(32))); } out.push_str(&format!(" printf(\" let {n} = u32x8::new(%u")); for _ in 1..8 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..8 { out.push_str(&format!(", {v}.i32[{i}]")); } } "UV2DI" => { for i in 0..2 { out.push_str(&format!(" {v}.i64[{i}] = {}UL;\n", rand_u64())); } out.push_str(&format!(" printf(\" let {n} = u64x2::new(%lu")); for _ in 1..2 { out.push_str(", %lu"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..2 { out.push_str(&format!(", {v}.i64[{i}]")); } } "UV4DI" => { for i in 0..4 { out.push_str(&format!(" {v}.i64[{i}] = {}UL;\n", rand_u64())); } out.push_str(&format!(" printf(\" let {n} = u64x4::new(%lu")); for _ in 1..4 { out.push_str(", %lu"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i64[{i}]")); } } "V4SF" => { for i in 0..4 { out.push_str(&format!(" {v}.f32[{i}] = {};\n", rand_f32())); } out.push_str(&format!(" printf(\" let {n} = u32x4::new(%u")); for _ in 1..4 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i32[{i}]")); } } "V8SF" => { for i in 0..8 { out.push_str(&format!(" {v}.f32[{i}] = {};\n", rand_f32())); } out.push_str(&format!(" printf(\" let {n} = u32x8::new(%u")); for _ in 1..8 { out.push_str(", %u"); } out.push_str(&format!(");\\n\",\n {v}.i32[0]")); for i in 1..8 { out.push_str(&format!(", {v}.i32[{i}]")); } } "V2DF" => { for i in 0..2 { out.push_str(&format!(" {v}.f64[{i}] = {};\n", rand_f64())); } out.push_str(&format!(" printf(\" let {n} = u64x2::new(%lu")); for _ in 1..2 { out.push_str(", %lu"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..2 { out.push_str(&format!(", {v}.i64[{i}]")); } } "V4DF" => { for i in 0..4 { out.push_str(&format!(" {v}.f64[{i}] = {};\n", rand_f64())); } out.push_str(&format!(" printf(\" let {n} = u64x4::new(%lu")); for _ in 1..4 { out.push_str(", %lu"); } out.push_str(&format!(");\\n\",\n {v}.i64[0]")); for i in 1..4 { out.push_str(&format!(", {v}.i64[{i}]")); } } "SI" | "DI" | "USI" | "UDI" | "UQI" | "QI" | "CVPOINTER" | "HI" => (), _ => panic!("unknown type: {t}"), } if !out.is_empty() { out.push_str(");"); } out }; let type_to_rp = |t: &str| -> &str { match t { "SI" => " printf(\" let r: i32 = %d;\\n\", o);", "DI" => " printf(\" let r: i64 = %ld;\\n\", o);", "USI" => " printf(\" let r: u32 = %u;\\n\", o);", "UDI" => " printf(\" let r: u64 = %lu;\\n\", o);", "UQI" => " printf(\" let r: u32 = %u;\\n\", o);", "QI" => " printf(\" let r: i32 = %d;\\n\", o);", "HI" => " printf(\" let r: i32 = %d;\\n\", o);", "V32QI" | "V16HI" | "V8SI" | "V4DI" | "UV32QI" | "UV16HI" | "UV8SI" | "UV4DI" | "V8SF" | "V4DF" => { " printf(\" let r = i64x4::new(%ld, %ld, %ld, %ld);\\n\", o.i64[0], o.i64[1], o.i64[2], o.i64[3]);" } _ => " printf(\" let r = i64x2::new(%ld, %ld);\\n\", o.i64[0], o.i64[1]);", } }; let type_to_rx = |t: &str| -> &str { match t { "SI" | "DI" | "USI" | "UDI" | "UQI" | "QI" | "HI" => "o", _ => "o.v", } }; let type_to_imm = |t| -> i8 { match t { 'b' => 4, 'h' => 3, 'w' => 2, 'd' => 1, _ => panic!("unsupported type"), } }; let impl_function = { let fn_output = if out_t.to_lowercase() == "void" { String::new() } else { format!(" {} o;", type_to_ct(out_t)) }; let mut fn_inputs = match para_num { 1 => format!( " {} a;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]) ), 2 => format!( " {} a;\n{}\n {} b;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), type_to_ct(in_t[1]), type_to_va("b", in_t[1]) ), 3 => format!( " {} a;\n{}\n {} b;\n{}\n {} c;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), type_to_ct(in_t[1]), type_to_va("b", in_t[1]), type_to_ct(in_t[2]), type_to_va("c", in_t[2]) ), 4 => format!( " {} a;\n{}\n {} b;\n{}\n {} c;\n{}\n {} d;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), type_to_ct(in_t[1]), type_to_va("b", in_t[1]), type_to_ct(in_t[2]), type_to_va("c", in_t[2]), type_to_ct(in_t[3]), type_to_va("d", in_t[3]) ), _ => panic!("unsupported parameter number"), }; let mut fn_params = match para_num { 1 => "(a.v)".to_string(), 2 => "(a.v, b.v)".to_string(), 3 => "(a.v, b.v, c.v)".to_string(), 4 => "(a.v, b.v, c.v, d.v)".to_string(), _ => "unsupported parameter number".to_string(), }; let mut as_params = match para_num { 1 => "(transmute(a))".to_string(), 2 => "(transmute(a), transmute(b))".to_string(), 3 => "(transmute(a), transmute(b), transmute(c))".to_string(), 4 => "(transmute(a), transmute(b), transmute(c), transmute(d))".to_string(), _ => panic!("unsupported parameter number"), }; let mut as_args = String::new(); if para_num == 1 && in_t[0] == "HI" { fn_inputs = "".to_string(); match asm_fmts[1].as_str() { "si13" => { let val = rand_i32(13); fn_params = format!("({val})"); as_params = format!("::<{val}>()"); } "i13" => { let val = rand_u32(12); fn_params = format!("({val})"); as_params = format!("::<{val}>()"); } "si10" => { let val = rand_i32(10); fn_params = format!("({val})"); as_params = format!("::<{val}>()"); } _ => panic!("unsupported assembly format: {}", asm_fmts[1]), } } else if para_num == 1 && (in_t[0] == "SI" || in_t[0] == "DI") && asm_fmts[1].starts_with("rj") { fn_params = "(a)".to_string(); if in_t[0] == "SI" { as_params = "(%d)".to_string(); } else { as_params = "(%ld)".to_string(); } as_args = ", a".to_string(); } else if para_num == 2 && (in_t[1] == "UQI" || in_t[1] == "USI") { if asm_fmts[2].starts_with("ui") { fn_inputs = format!( " {} a;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]) ); let val = rand_u32(asm_fmts[2].get(2..).unwrap().parse::().unwrap()); fn_params = format!("(a.v, {val})"); as_params = format!("::<{val}>(transmute(a))"); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 2 && in_t[1] == "QI" { if asm_fmts[2].starts_with("si") { fn_inputs = format!( " {} a;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]) ); let val = rand_i32(asm_fmts[2].get(2..).unwrap().parse::().unwrap()); fn_params = format!("(a.v, {val})"); as_params = format!("::<{val}>(transmute(a))"); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 2 && in_t[1] == "SI" && asm_fmts[2].starts_with("rk") { fn_params = "(a.v, b)".to_string(); as_params = "(transmute(a), %d)".to_string(); as_args = ", b".to_string(); } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "SI" { if asm_fmts[2].starts_with("si") { fn_inputs = format!( " union v{}qi _a;\n{}\n {} a = &_a;", target.bytes(), type_to_va( "_a", if target == TargetFeature::Lsx { "A16QI" } else { "A32QI" } ), type_to_ct(in_t[0]) ); fn_params = "(a, 0)".to_string(); as_params = "::<0>(a.as_ptr())".to_string(); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 2 && in_t[0] == "CVPOINTER" && in_t[1] == "DI" { if asm_fmts[2].as_str() == "rk" { fn_inputs = format!( " union v{}qi _a;\n{}\n {} a = &_a;", target.bytes(), type_to_va( "_a", if target == TargetFeature::Lsx { "A16QI" } else { "A32QI" } ), type_to_ct(in_t[0]) ); fn_params = "(a, 0)".to_string(); as_params = "(a.as_ptr(), 0)".to_string(); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 3 && in_t[2] == "UQI" && asm_fmts[1].starts_with("rj") { if asm_fmts[2].starts_with("ui") { fn_inputs = format!( " {} a;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]) ); let ival = rand_i32(32); let uval = rand_u32(asm_fmts[2].get(2..).unwrap().parse::().unwrap()); fn_params = format!("(a.v, {ival}, {uval})"); as_params = format!("::<{uval}>(transmute(a), {ival})"); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 3 && (in_t[2] == "USI" || in_t[2] == "UQI") { if asm_fmts[2].starts_with("ui") { fn_inputs = format!( " {} a;\n{}\n {} b;\n{}", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), type_to_ct(in_t[1]), type_to_va("b", in_t[1]), ); let val = rand_u32(asm_fmts[2].get(2..).unwrap().parse::().unwrap()); fn_params = format!("(a.v, b.v, {val})"); as_params = format!("::<{val}>(transmute(a), transmute(b))"); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "SI" { if asm_fmts[2].as_str() == "si12" { fn_inputs = format!( " {} a;\n{}\n union v{}qi o;\n{}\n {} b = &o;", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), target.bytes(), type_to_va( "o", if target == TargetFeature::Lsx { "AM16QI" } else { "AM32QI" } ), type_to_ct(in_t[1]) ); fn_params = "(a.v, b, 0)".to_string(); as_params = "::<0>(transmute(a), o.as_mut_ptr())".to_string(); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 3 && in_t[1] == "CVPOINTER" && in_t[2] == "DI" { if asm_fmts[2].as_str() == "rk" { fn_inputs = format!( " {} a;\n{}\n union v{}qi o;\n{}\n {} b = &o;", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), target.bytes(), type_to_va( "o", if target == TargetFeature::Lsx { "AM16QI" } else { "AM32QI" } ), type_to_ct(in_t[1]) ); fn_params = "(a.v, b, 0)".to_string(); as_params = "(transmute(a), o.as_mut_ptr(), 0)".to_string(); } else { panic!("unsupported assembly format: {}", asm_fmts[2]); } } else if para_num == 4 { match (asm_fmts[2].as_str(), current_name.chars().last().unwrap()) { ("si8", t) => { fn_inputs = format!( " {} a;\n{}\n union v{}qi o;\n{}\n {} b = &o;", type_to_ct(in_t[0]), type_to_va("a", in_t[0]), target.bytes(), type_to_va( "o", if target == TargetFeature::Lsx { "AM16QI" } else { "AM32QI" } ), type_to_ct(in_t[1]) ); let val = rand_u32(type_to_imm(t).try_into().unwrap()); fn_params = format!("(a.v, b, 0, {val})"); as_params = format!("::<0, {val}>(transmute(a), o.as_mut_ptr())"); } (_, _) => panic!( "unsupported assembly format: {} for {}", asm_fmts[2], current_name ), }; } let fn_docall = if out_t.to_lowercase() == "void" { format!(" __{current_name}{fn_params};") } else { format!(" {} = __{current_name}{fn_params};", type_to_rx(out_t)) }; let fn_result = if out_t.to_lowercase() == "void" { if target == TargetFeature::Lsx { type_to_rp("V16QI") } else { type_to_rp("V32QI") } } else { type_to_rp(out_t) }; let fn_assert = { if out_t.to_lowercase() == "void" { format!( " printf(\"\\n {current_name}{as_params};\\n assert_eq!(r, transmute(o));\\n\"{as_args});" ) } else { format!( " printf(\"\\n assert_eq!(r, transmute({current_name}{as_params}));\\n\"{as_args});" ) } }; format!( r#" static void {current_name}(void) {{ printf("\n#[simd_test(enable = \"{}\")]\n"); printf("unsafe fn test_{current_name}() {{\n"); {fn_inputs} {fn_output} {fn_docall} {fn_result} {fn_assert} printf("}}\n"); }} "#, target.as_target_feature_arg(current_name) ) }; let call_function = format!(" {current_name}();\n"); (impl_function, call_function) } pub fn main() -> io::Result<()> { let args: Vec = env::args().collect(); let in_file = args.get(1).cloned().expect("Input file missing!"); let in_file_path = PathBuf::from(&in_file); let in_file_name = in_file_path .file_name() .unwrap() .to_os_string() .into_string() .unwrap(); let ext_name = if in_file_name.starts_with("lasx") { "lasx" } else { "lsx" }; if in_file_name.ends_with(".h") { gen_spec(in_file, ext_name) } else if args.get(2).is_some() { gen_test(in_file, ext_name) } else { gen_bind(in_file, ext_name) } }