blob: 5076064ffcdd34693a6c7d638219a4c8a994cb5f [file] [log] [blame]
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<String>,
}
impl Lines {
fn single(line: String) -> Self {
Self::from(vec![line])
}
}
impl From<Vec<String>> for Lines {
fn from(lines: Vec<String>) -> 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<String> = None;
let mut asm_fmts: Vec<String> = 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(&current_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!(
"<const IMM_S13: {}>()",
type_to_rst(in_t[0], is_store, Intrinsic)
),
"si10" => format!(
"<const IMM_S10: {}>()",
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!(
"<const IMM{2}: {1}>(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!(
"<const IMM_S{2}: {1}>(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!(
"<const IMM_S{2}: {1}>(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!(
"<const IMM{3}: {2}>(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!(
"<const IMM_S12: {2}>(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!(
"<const IMM_S8: {2}, const IMM{4}: {3}>(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<String> = None;
let mut asm_fmts: Vec<String> = 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 <stdio.h>
#include <stdint.h>
#include <lsxintrin.h>
#include <lasxintrin.h>
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(&current_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::<i32>();
let bits = 32 - bits;
(val << bits) >> bits
};
let rand_u32 = |bits: u8| -> u32 {
let val = rand::random::<u32>();
let bits = 32 - bits;
(val << bits) >> bits
};
let rand_i64 = || -> i64 { rand::random::<i64>() };
let rand_u64 = || -> u64 { rand::random::<u64>() };
let rand_f32 = || -> f32 { rand::random::<f32>() };
let rand_f64 = || -> f64 { rand::random::<f64>() };
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::<u8>().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::<u8>().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::<u8>().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::<u8>().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<String> = 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)
}
}