From a9835887404c55caf7c73ba3f99abd9d04bae098 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 15 Sep 2026 00:08:33 +0800 Subject: [PATCH 1/3] intrinsic-test: Fix intrinsic test names for negative immediate values --- crates/intrinsic-test/src/common/gen_c.rs | 11 ++++++++--- crates/intrinsic-test/src/common/gen_rust.rs | 13 +++++++++---- crates/intrinsic-test/src/common/mod.rs | 5 +++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/intrinsic-test/src/common/gen_c.rs b/crates/intrinsic-test/src/common/gen_c.rs index bbff7a91b6..30e7bb1d37 100644 --- a/crates/intrinsic-test/src/common/gen_c.rs +++ b/crates/intrinsic-test/src/common/gen_c.rs @@ -1,5 +1,6 @@ use itertools::Itertools; +use crate::common::imm_value_to_ident; use crate::common::{SupportedArchitecture, intrinsic::Intrinsic}; use super::intrinsic_helpers::TypeDefinition; @@ -41,9 +42,13 @@ void {name}_wrapper{imm_arglist}({return_ty}* __dst{arglist}) {{ }}", return_ty = intrinsic.results.c_type(), name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + imm_arglist = + imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!( + "_{}", + imm_value_to_ident(i) + ))), arglist = intrinsic.arguments.as_non_imm_arglist_c(), params = intrinsic.arguments.as_call_params_c(&imm_values) )) diff --git a/crates/intrinsic-test/src/common/gen_rust.rs b/crates/intrinsic-test/src/common/gen_rust.rs index bfe37edbcf..f319418b49 100644 --- a/crates/intrinsic-test/src/common/gen_rust.rs +++ b/crates/intrinsic-test/src/common/gen_rust.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; +use crate::common::imm_value_to_ident; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; @@ -276,7 +277,7 @@ for (id, rust, c) in specializations {{ } }) .join(","), - c_const_args = imm_values.iter().join("_"), + c_const_args = imm_values.iter().map(imm_value_to_ident).join("_"), )) } }), @@ -343,9 +344,13 @@ unsafe extern "C" {{ "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", return_ty = intrinsic.results.rust_type(), name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + imm_arglist = + imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!( + "_{}", + imm_value_to_ident(i) + ))), arglist = intrinsic.arguments.as_non_imm_arglist_rust(), )) })) diff --git a/crates/intrinsic-test/src/common/mod.rs b/crates/intrinsic-test/src/common/mod.rs index b476ad477c..e89599d06e 100644 --- a/crates/intrinsic-test/src/common/mod.rs +++ b/crates/intrinsic-test/src/common/mod.rs @@ -138,3 +138,8 @@ pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { let number_of_chunks = intrinsic_count.div_ceil(max_intrinsics_per_chunk); (max_intrinsics_per_chunk, number_of_chunks) } + +pub fn imm_value_to_ident(value: impl std::fmt::Display) -> String { + let value = value.to_string(); + value.replace('-', "neg") +} From 9e1bae80f46d9e7915c8bbff7c243f203db1ef2c Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 15 Sep 2026 00:08:34 +0800 Subject: [PATCH 2/3] intrinsic-test: Add C intrinsic name prefix support Add a per-architecture C intrinsic name prefix to support architectures whose C intrinsic names differ from their Rust intrinsic names. --- crates/intrinsic-test/src/arm/mod.rs | 2 ++ crates/intrinsic-test/src/common/gen_c.rs | 3 ++- crates/intrinsic-test/src/common/mod.rs | 4 ++++ crates/intrinsic-test/src/x86/mod.rs | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs index 9c2d0f336a..5e892c6134 100644 --- a/crates/intrinsic-test/src/arm/mod.rs +++ b/crates/intrinsic-test/src/arm/mod.rs @@ -37,6 +37,8 @@ impl SupportedArchitecture for Arm { "#; const RUST_PRELUDE: &str = RUST_PRELUDE; + const C_NAME_PREFIX: &str = ""; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name let big_endian = cli_options.target.starts_with("aarch64_be"); diff --git a/crates/intrinsic-test/src/common/gen_c.rs b/crates/intrinsic-test/src/common/gen_c.rs index 30e7bb1d37..a749afd17d 100644 --- a/crates/intrinsic-test/src/common/gen_c.rs +++ b/crates/intrinsic-test/src/common/gen_c.rs @@ -38,9 +38,10 @@ pub fn write_wrapper_c( fmt(&format_args!( " void {name}_wrapper{imm_arglist}({return_ty}* __dst{arglist}) {{ - *__dst = {name}({params}); + *__dst = {prefix}{name}({params}); }}", return_ty = intrinsic.results.c_type(), + prefix = A::C_NAME_PREFIX, name = intrinsic.name, imm_arglist = imm_values diff --git a/crates/intrinsic-test/src/common/mod.rs b/crates/intrinsic-test/src/common/mod.rs index e89599d06e..9cde80daff 100644 --- a/crates/intrinsic-test/src/common/mod.rs +++ b/crates/intrinsic-test/src/common/mod.rs @@ -49,6 +49,10 @@ pub trait SupportedArchitecture: Sized { const C_PRELUDE: &str; const RUST_PRELUDE: &str; + /// Per-architecture prefix used to convert a Rust intrinsic name to the + /// corresponding C intrinsic name by prepending it to the Rust name. + const C_NAME_PREFIX: &str; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { diff --git a/crates/intrinsic-test/src/x86/mod.rs b/crates/intrinsic-test/src/x86/mod.rs index 36f4fee437..9eafcf1fc7 100644 --- a/crates/intrinsic-test/src/x86/mod.rs +++ b/crates/intrinsic-test/src/x86/mod.rs @@ -32,6 +32,8 @@ impl SupportedArchitecture for X86 { "#; const RUST_PRELUDE: &str = RUST_PRELUDE; + const C_NAME_PREFIX: &str = ""; + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ "-maes", From 5694c574751c26d2db6ecf3d6030dbd36450aaa7 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 14 Sep 2026 11:32:27 +0800 Subject: [PATCH 3/3] intrinsic-test: Support testing LoongArch64 SIMD intrinsics --- .github/workflows/main.yml | 1 + .../loongarch64-unknown-linux-gnu/Dockerfile | 9 +- ci/intrinsic-test.sh | 14 + .../missing_loongarch64_clang.txt | 0 .../missing_loongarch64_common.txt | 31 ++ .../missing_loongarch64_gcc.txt | 0 .../intrinsic-test/src/loongarch/intrinsic.rs | 19 ++ crates/intrinsic-test/src/loongarch/mod.rs | 139 ++++++++ crates/intrinsic-test/src/loongarch/parser.rs | 179 ++++++++++ crates/intrinsic-test/src/loongarch/types.rs | 309 ++++++++++++++++++ crates/intrinsic-test/src/main.rs | 7 + 11 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 crates/intrinsic-test/missing_loongarch64_clang.txt create mode 100644 crates/intrinsic-test/missing_loongarch64_common.txt create mode 100644 crates/intrinsic-test/missing_loongarch64_gcc.txt create mode 100644 crates/intrinsic-test/src/loongarch/intrinsic.rs create mode 100644 crates/intrinsic-test/src/loongarch/mod.rs create mode 100644 crates/intrinsic-test/src/loongarch/parser.rs create mode 100644 crates/intrinsic-test/src/loongarch/types.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c2e2ae4a8..51a37375d1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -279,6 +279,7 @@ jobs: - aarch64-unknown-linux-gnu - aarch64_be-unknown-linux-gnu - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu - x86_64-unknown-linux-gnu profile: [dev, release] cc: [clang, gcc] diff --git a/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index e803dae100..8cbd2d8080 100644 --- a/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -3,8 +3,15 @@ FROM ubuntu:25.10 RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc libc6-dev qemu-user ca-certificates \ - gcc-loongarch64-linux-gnu libc6-dev-loong64-cross + gcc-loongarch64-linux-gnu libc6-dev-loong64-cross \ + wget +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN mkdir llvm +RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm + +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=loongarch64-linux-gnu-gcc ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc \ CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64 -cpu max -L /usr/loongarch64-linux-gnu" \ diff --git a/ci/intrinsic-test.sh b/ci/intrinsic-test.sh index 3966aa88d8..4b7a3d27ea 100755 --- a/ci/intrinsic-test.sh +++ b/ci/intrinsic-test.sh @@ -65,6 +65,12 @@ case ${TARGET} in ARCH=x86 RUNTIME_RUSTFLAGS= ;; + + loongarch64*) + export CFLAGS="-I/usr/loongarch64-linux-gnu/include/" + ARCH=loongarch64 + RUNTIME_RUSTFLAGS=-Ctarget-feature=+lsx,+lasx,+frecipe + ;; *) ;; @@ -80,6 +86,14 @@ case "${TARGET}" in --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; + loongarch64*) + cargo run "${INTRINSIC_TEST}" --release \ + --bin intrinsic-test -- crates/stdarch-gen-loongarch \ + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ + --cc-arg-style "${CC_ARG_STYLE}" + ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ diff --git a/crates/intrinsic-test/missing_loongarch64_clang.txt b/crates/intrinsic-test/missing_loongarch64_clang.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/intrinsic-test/missing_loongarch64_common.txt b/crates/intrinsic-test/missing_loongarch64_common.txt new file mode 100644 index 0000000000..5817247c2f --- /dev/null +++ b/crates/intrinsic-test/missing_loongarch64_common.txt @@ -0,0 +1,31 @@ +# Missing in old c compiler +lasx_concat_128 +lasx_concat_128_d +lasx_concat_128_s +lasx_extract_128_hi +lasx_extract_128_lo +lasx_extract_128_hi_d +lasx_extract_128_lo_d +lasx_extract_128_hi_s +lasx_extract_128_lo_s +lasx_insert_128_hi +lasx_insert_128_lo +lasx_insert_128_hi_d +lasx_insert_128_lo_d +lasx_insert_128_hi_s +lasx_insert_128_lo_s + +# Missing in old qemu +lasx_xvfrecipe_d +lasx_xvfrecipe_s +lasx_xvfrsqrte_d +lasx_xvfrsqrte_s +lsx_vfrecipe_d +lsx_vfrecipe_s +lsx_vfrsqrte_d +lsx_vfrsqrte_s + +# Top bits are undefined, unclear how to test these +lasx_cast_128 +lasx_cast_128_d +lasx_cast_128_s diff --git a/crates/intrinsic-test/missing_loongarch64_gcc.txt b/crates/intrinsic-test/missing_loongarch64_gcc.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/intrinsic-test/src/loongarch/intrinsic.rs b/crates/intrinsic-test/src/loongarch/intrinsic.rs new file mode 100644 index 0000000000..f8ebb3e7ab --- /dev/null +++ b/crates/intrinsic-test/src/loongarch/intrinsic.rs @@ -0,0 +1,19 @@ +use crate::common::intrinsic_helpers::IntrinsicType; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone, PartialEq)] +pub struct LoongArchType(pub IntrinsicType); + +impl Deref for LoongArchType { + type Target = IntrinsicType; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for LoongArchType { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/crates/intrinsic-test/src/loongarch/mod.rs b/crates/intrinsic-test/src/loongarch/mod.rs new file mode 100644 index 0000000000..00cff20a0f --- /dev/null +++ b/crates/intrinsic-test/src/loongarch/mod.rs @@ -0,0 +1,139 @@ +mod intrinsic; +mod parser; +mod types; + +use std::path::Path; + +use crate::common::SupportedArchitecture; +use crate::common::cli::ProcessedCli; +use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_helpers::TypeKind; +use intrinsic::LoongArchType; +use parser::get_intrinsics; + +#[derive(PartialEq)] +pub struct LoongArch { + intrinsics: Vec>, +} + +impl SupportedArchitecture for LoongArch { + type Type = LoongArchType; + + fn intrinsics(&self) -> &[Intrinsic] { + &self.intrinsics + } + + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from LoongArch specification files, published under the same license as the +// `intrinsic-test` crate. +"#; + + const C_PRELUDE: &str = r#" +#include +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; + + const C_NAME_PREFIX: &str = "__"; + + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { + let mut flags = vec!["-mlsx"]; + if self + .intrinsics + .iter() + .any(|intrinsic| intrinsic.extension == "LASX") + { + flags.push("-mlasx"); + } + if self.intrinsics.iter().any(|intrinsic| { + intrinsic.name.contains("frecipe") || intrinsic.name.contains("frsqrte") + }) { + flags.push("-mfrecipe"); + } + flags + } + + fn create(cli_options: &ProcessedCli) -> Self { + let mut intrinsics = + load_intrinsics(&cli_options.filename).expect("Error parsing input file"); + + intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); + intrinsics.dedup_by(|a, b| { + a.name == b.name && a.results == b.results && a.arguments == b.arguments + }); + + let intrinsics = intrinsics + .into_iter() + // Skip intrinsics that don't return a value. + .filter(|intrinsic| intrinsic.results.kind() != TypeKind::Void) + .filter(|intrinsic| !intrinsic.arguments.args.is_empty()) + // Skip pointers for now, we would probably need to look at the return + // type to work out how many elements we need to point to. + .filter(|intrinsic| !intrinsic.arguments.iter().any(|arg| arg.is_ptr())) + // Skip intrinsics from `--skip` + .filter(|intrinsic| !cli_options.skip.contains(&intrinsic.name)) + .collect::>(); + + let sample_percentage: usize = cli_options.sample_percentage as usize; + let sample_size = (intrinsics.len() * sample_percentage) / 100; + let intrinsics = intrinsics.into_iter().take(sample_size).collect(); + + Self { intrinsics } + } + + fn predicate_function(_: u32) -> String { + unimplemented!("no scalable vectors on LoongArch") + } +} + +fn load_intrinsics(path: &Path) -> Result>, Box> { + if path.is_dir() { + let mut intrinsics = Vec::new(); + for spec in ["lsx.spec", "lasx.spec"] { + let spec_path = path.join(spec); + if spec_path.exists() { + intrinsics.extend(get_intrinsics(&spec_path)?); + } + } + return Ok(intrinsics); + } + + get_intrinsics(path) +} + +const RUST_PRELUDE: &str = r#" +#![feature(stdarch_loongarch)] + +use core_arch::arch::loongarch64::*; + +#[inline] +unsafe fn lsx_vld_to_m128i(mem_addr: *const i8) -> m128i { + lsx_vld::<0>(mem_addr) +} + +#[inline] +unsafe fn lsx_vld_to_m128(mem_addr: *const i8) -> m128 { + core::mem::transmute(lsx_vld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lsx_vld_to_m128d(mem_addr: *const i8) -> m128d { + core::mem::transmute(lsx_vld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lasx_xvld_to_m256i(mem_addr: *const i8) -> m256i { + lasx_xvld::<0>(mem_addr) +} + +#[inline] +unsafe fn lasx_xvld_to_m256(mem_addr: *const i8) -> m256 { + core::mem::transmute(lasx_xvld::<0>(mem_addr)) +} + +#[inline] +unsafe fn lasx_xvld_to_m256d(mem_addr: *const i8) -> m256d { + core::mem::transmute(lasx_xvld::<0>(mem_addr)) +} +"#; diff --git a/crates/intrinsic-test/src/loongarch/parser.rs b/crates/intrinsic-test/src/loongarch/parser.rs new file mode 100644 index 0000000000..ecb5d1ab3e --- /dev/null +++ b/crates/intrinsic-test/src/loongarch/parser.rs @@ -0,0 +1,179 @@ +use std::path::Path; + +use super::intrinsic::LoongArchType; +use super::types::parse_intrinsic_type; +use crate::common::argument::{Argument, ArgumentList}; +use crate::common::constraint::Constraint; +use crate::common::intrinsic::Intrinsic; +use crate::loongarch::LoongArch; + +pub fn get_intrinsics( + filename: &Path, +) -> Result>, Box> { + parse_spec_file(filename) +} + +fn parse_spec_file( + filename: &Path, +) -> Result>, Box> { + let contents = std::fs::read_to_string(filename)?; + parse_spec_contents(&contents) +} + +fn parse_spec_contents( + contents: &str, +) -> Result>, Box> { + let mut intrinsics = Vec::new(); + let mut record = Vec::new(); + + for line in contents.lines().chain(std::iter::once("")) { + let line = line.trim(); + if line.is_empty() { + if !record.is_empty() { + if let Some(intrinsic) = parse_record(&record)? { + intrinsics.push(intrinsic); + } + record.clear(); + } + continue; + } + record.push(line); + } + + Ok(intrinsics) +} + +fn parse_record( + record: &[&str], +) -> Result>, Box> { + let mut name = None; + let mut asm_formats = Vec::new(); + let mut data_types = None; + + for line in record { + if let Some(value) = line.strip_prefix("name = ") { + name = Some(value.to_string()); + } else if let Some(value) = line.strip_prefix("asm-fmts = ") { + asm_formats = value + .split(',') + .map(|part| part.trim().to_string()) + .collect(); + } else if let Some(value) = line.strip_prefix("data-types = ") { + data_types = Some(value); + } + } + + let Some(data_types) = data_types else { + return Ok(None); + }; + + let name = name.ok_or("missing name before data-types")?; + Ok(Some(parse_intrinsic(&name, &asm_formats, data_types)?)) +} + +fn parse_intrinsic( + name: &str, + asm_formats: &[String], + data_types: &str, +) -> Result, Box> { + let data_types = data_types + .split(',') + .map(|value| value.trim()) + .filter(|value| !value.is_empty()) + .collect::>(); + let Some((result_type, argument_types)) = data_types.split_first() else { + return Err("missing data-types for intrinsic".into()); + }; + let result = LoongArchType(parse_intrinsic_type(result_type)?); + let asm_offset = asm_formats + .len() + .checked_sub(argument_types.len()) + .ok_or_else(|| format!("{name}: fewer asm formats than arguments"))?; + let arguments = argument_types + .iter() + .enumerate() + .map(|(pos, data_type)| { + let constraint = asm_formats + .get(pos + asm_offset) + .and_then(|format| parse_constraint(name, format)); + let mut ty = LoongArchType(parse_intrinsic_type(data_type)?); + if constraint.is_some() { + ty.constant = true; + } + Ok(Argument::new( + pos, + format!("arg_{pos}"), + ty, + constraint, + false, + )) + }) + .collect::, String>>()?; + + Ok(Intrinsic { + name: name.to_string(), + arguments: ArgumentList { args: arguments }, + results: result, + arch_tags: Vec::new(), + extension: extension_for(name)?.to_string(), + }) +} + +fn parse_constraint(name: &str, asm_format: &str) -> Option { + if let Some(cons) = special_constraint(name) { + return Some(cons); + } + if let Some(bits) = asm_format.strip_prefix("ui") { + return Some(unsigned_constraint(bits.parse::().ok()?)); + } + if let Some(bits) = asm_format + .strip_prefix("si") + .or_else(|| asm_format.strip_prefix('i')) + { + return Some(signed_constraint(bits.parse::().ok()?)); + } + None +} + +fn special_constraint(name: &str) -> Option { + match name { + "lsx_vldi" | "lasx_xvldi" => { + // CC: imm13 only support 0000 ~ 1100 in bits 9 ~ 12 when bit ‘13’ is 1 + let values: Vec = (0i64..8192i64) + .filter(|&x| x < 4096 || ((x >> 8) & 0xf) < 13) + // sign extend + .map(|x| ((x << 51) as i64) >> 51) + .collect(); + Some(Constraint::Set(values)) + } + _ => None, + } +} + +fn unsigned_constraint(bits: u32) -> Constraint { + Constraint::Range(0..(1i64 << bits.min(63))) +} + +fn signed_constraint(bits: u32) -> Constraint { + let min = if bits == 64 { + i64::MIN + } else { + -(1i64 << (bits - 1)) + }; + let max = if bits == 64 { + i64::MAX + } else { + 1i64 << (bits - 1) + }; + Constraint::Range(min..max) +} + +fn extension_for(name: &str) -> Result<&'static str, Box> { + if name.starts_with("lasx_") { + Ok("LASX") + } else if name.starts_with("lsx_") { + Ok("LSX") + } else { + Err(format!("unsupported LoongArch intrinsic name {name}").into()) + } +} diff --git a/crates/intrinsic-test/src/loongarch/types.rs b/crates/intrinsic-test/src/loongarch/types.rs new file mode 100644 index 0000000000..dd4e578e67 --- /dev/null +++ b/crates/intrinsic-test/src/loongarch/types.rs @@ -0,0 +1,309 @@ +use super::intrinsic::LoongArchType; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; + +impl TypeDefinition for LoongArchType { + fn c_type(&self) -> String { + if self.ptr { + return if self.ptr_constant { + "const void*".to_string() + } else { + "void*".to_string() + }; + } + + match (self.kind(), self.simd_len) { + (_, Some(SimdLen::Fixed(lanes))) => { + format!( + "__{}", + vector_type_name(lanes, self.inner_size(), self.kind()) + ) + } + (TypeKind::Int(Sign::Signed), None) => { + scalar_type_name(true, scalar_signature_bits(self.inner_size())).to_string() + } + (TypeKind::Int(Sign::Unsigned), None) => { + scalar_type_name(false, scalar_signature_bits(self.inner_size())).to_string() + } + (TypeKind::Float, None) => match self.inner_size() { + 32 => "float".to_string(), + 64 => "double".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + (TypeKind::Void, None) => "void".to_string(), + _ => unreachable!("unsupported LoongArch type {self:#?}"), + } + } + + fn rust_type(&self) -> String { + if self.ptr { + return format!( + "*{} core::ffi::c_void", + if self.ptr_constant { "const" } else { "mut" }, + ); + } + + match (self.kind(), self.simd_len) { + (_, Some(SimdLen::Fixed(lanes))) => { + vector_type_name(lanes, self.inner_size(), self.kind()).to_string() + } + (TypeKind::Int(Sign::Signed), None) => { + semantic_rust_scalar_type(true, scalar_signature_bits(self.inner_size())) + .to_string() + } + (TypeKind::Int(Sign::Unsigned), None) => { + semantic_rust_scalar_type(false, scalar_signature_bits(self.inner_size())) + .to_string() + } + (TypeKind::Float, None) => match self.inner_size() { + 32 => "f32".to_string(), + 64 => "f64".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + (TypeKind::Void, None) => "()".to_string(), + _ => unreachable!("unsupported LoongArch type {self:#?}"), + } + } + + fn rust_scalar_type(&self) -> String { + match self.kind() { + TypeKind::Int(Sign::Signed) => { + semantic_rust_scalar_type(true, self.inner_size()).to_string() + } + TypeKind::Int(Sign::Unsigned) => { + semantic_rust_scalar_type(false, self.inner_size()).to_string() + } + TypeKind::Float => match self.inner_size() { + 32 => "f32".to_string(), + 64 => "f64".to_string(), + bits => unreachable!("unsupported scalar float width {bits}"), + }, + _ => unreachable!("unsupported LoongArch scalar type {self:#?}"), + } + } + + fn load_function(&self) -> String { + let Some(SimdLen::Fixed(lanes)) = self.simd_len else { + unreachable!("LoongArch loads are only used for SIMD types") + }; + + match (lanes * self.inner_size(), self.kind()) { + (128, TypeKind::Float) if self.inner_size() == 64 => "lsx_vld_to_m128d".to_string(), + (128, TypeKind::Float) => "lsx_vld_to_m128".to_string(), + (128, _) => "lsx_vld_to_m128i".to_string(), + (256, TypeKind::Float) if self.inner_size() == 64 => "lasx_xvld_to_m256d".to_string(), + (256, TypeKind::Float) => "lasx_xvld_to_m256".to_string(), + (256, _) => "lasx_xvld_to_m256i".to_string(), + bits => unreachable!("unsupported LoongArch vector width {bits:?}"), + } + } +} + +fn vector_type_name(lanes: u32, bit_len: u32, kind: TypeKind) -> &'static str { + match (lanes * bit_len, kind, bit_len) { + (128, TypeKind::Float, 32) => "m128", + (128, TypeKind::Float, 64) => "m128d", + (128, _, _) => "m128i", + (256, TypeKind::Float, 32) => "m256", + (256, TypeKind::Float, 64) => "m256d", + (256, _, _) => "m256i", + _ => unreachable!("unsupported LoongArch vector shape {kind:?}x{bit_len}x{lanes}"), + } +} + +fn scalar_type_name(signed: bool, bit_len: u32) -> &'static str { + match (signed, bit_len) { + (true, 32) => "int32_t", + (true, 64) => "int64_t", + (false, 32) => "uint32_t", + (false, 64) => "uint64_t", + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +fn scalar_signature_bits(bit_len: u32) -> u32 { + match bit_len { + 8 | 16 | 32 => 32, + 64 => 64, + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +fn semantic_rust_scalar_type(signed: bool, bit_len: u32) -> &'static str { + match (signed, bit_len) { + (true, 8) => "i8", + (true, 16) => "i16", + (true, 32) => "i32", + (true, 64) => "i64", + (false, 8) => "u8", + (false, 16) => "u16", + (false, 32) => "u32", + (false, 64) => "u64", + _ => unreachable!("unsupported LoongArch scalar width {bit_len}"), + } +} + +pub fn parse_intrinsic_type(s: &str) -> Result { + let (kind, bit_len, simd_len, ptr, ptr_constant) = match s { + "V16QI" => ( + TypeKind::Int(Sign::Signed), + Some(8), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "V32QI" => ( + TypeKind::Int(Sign::Signed), + Some(8), + Some(SimdLen::Fixed(32)), + false, + false, + ), + "V8HI" => ( + TypeKind::Int(Sign::Signed), + Some(16), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V16HI" => ( + TypeKind::Int(Sign::Signed), + Some(16), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "V4SI" => ( + TypeKind::Int(Sign::Signed), + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V8SI" => ( + TypeKind::Int(Sign::Signed), + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V2DI" => ( + TypeKind::Int(Sign::Signed), + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "V4DI" => ( + TypeKind::Int(Sign::Signed), + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "UV16QI" => ( + TypeKind::Int(Sign::Unsigned), + Some(8), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "UV32QI" => ( + TypeKind::Int(Sign::Unsigned), + Some(8), + Some(SimdLen::Fixed(32)), + false, + false, + ), + "UV8HI" => ( + TypeKind::Int(Sign::Unsigned), + Some(16), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "UV16HI" => ( + TypeKind::Int(Sign::Unsigned), + Some(16), + Some(SimdLen::Fixed(16)), + false, + false, + ), + "UV4SI" => ( + TypeKind::Int(Sign::Unsigned), + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "UV8SI" => ( + TypeKind::Int(Sign::Unsigned), + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "UV2DI" => ( + TypeKind::Int(Sign::Unsigned), + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "UV4DI" => ( + TypeKind::Int(Sign::Unsigned), + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V4SF" => ( + TypeKind::Float, + Some(32), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "V8SF" => ( + TypeKind::Float, + Some(32), + Some(SimdLen::Fixed(8)), + false, + false, + ), + "V2DF" => ( + TypeKind::Float, + Some(64), + Some(SimdLen::Fixed(2)), + false, + false, + ), + "V4DF" => ( + TypeKind::Float, + Some(64), + Some(SimdLen::Fixed(4)), + false, + false, + ), + "QI" => (TypeKind::Int(Sign::Signed), Some(8), None, false, false), + "HI" => (TypeKind::Int(Sign::Signed), Some(16), None, false, false), + "SI" => (TypeKind::Int(Sign::Signed), Some(32), None, false, false), + "UQI" => (TypeKind::Int(Sign::Unsigned), Some(8), None, false, false), + "UHI" => (TypeKind::Int(Sign::Unsigned), Some(16), None, false, false), + "USI" => (TypeKind::Int(Sign::Unsigned), Some(32), None, false, false), + "DI" => (TypeKind::Int(Sign::Signed), Some(64), None, false, false), + "UDI" => (TypeKind::Int(Sign::Unsigned), Some(64), None, false, false), + "CVPOINTER" => (TypeKind::Int(Sign::Signed), Some(8), None, true, true), + "VOID" => (TypeKind::Void, None, None, false, false), + _ => return Err(format!("unsupported LoongArch type {s}")), + }; + + Ok(IntrinsicType { + constant: false, + ptr_constant, + ptr, + kind, + bit_len, + simd_len, + vec_len: None, + }) +} diff --git a/crates/intrinsic-test/src/main.rs b/crates/intrinsic-test/src/main.rs index e25eb48a45..91e76d8f5f 100644 --- a/crates/intrinsic-test/src/main.rs +++ b/crates/intrinsic-test/src/main.rs @@ -3,11 +3,13 @@ extern crate log; mod arm; mod common; +mod loongarch; mod x86; use arm::Arm; use common::SupportedArchitecture; use common::cli::{Cli, ProcessedCli}; +use loongarch::LoongArch; use x86::X86; fn main() { @@ -21,6 +23,11 @@ fn main() { run(Arm::create(&processed_cli_options), processed_cli_options) } else if processed_cli_options.target.starts_with("x86") { run(X86::create(&processed_cli_options), processed_cli_options) + } else if processed_cli_options.target.starts_with("loongarch64") { + run( + LoongArch::create(&processed_cli_options), + processed_cli_options, + ) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) }