Skip to content

Adding the x86 part of behavioural testing for std::arch intrinsics #1814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4a3d05f
fix: updated function definition of "from_c" in IntrinsicTypeDefinition
madhav-madhusoodanan May 30, 2025
38fd826
Feat: started the skeleton for x86 module, added XML intrinsic parsin…
madhav-madhusoodanan May 30, 2025
a786643
feat: added functionality to convert XML description of intrinsics to
madhav-madhusoodanan Jun 3, 2025
7b8e08c
feat: added the simple set of argument types for X86 intrinsics
madhav-madhusoodanan Jun 8, 2025
e95bd9b
feat: added X86IntrinsicType parsing from string.
madhav-madhusoodanan Jun 8, 2025
241f309
fix: removing Box<> types from IntrinsicType in "from_c" definition for
madhav-madhusoodanan Jun 8, 2025
c77800a
feat: implemented c_type for X86IntrinsicType
madhav-madhusoodanan Jun 8, 2025
287b83c
Sharpening the parsing logic:
madhav-madhusoodanan Jun 13, 2025
6ee8e57
feat: demote "target" to a 2nd class variable, since it is only rarely
madhav-madhusoodanan Jun 16, 2025
16bd12c
Add x86/config.rs to intrinsic-test
madhav-madhusoodanan Jun 16, 2025
e827a04
Fix: unused variables.
madhav-madhusoodanan Jun 16, 2025
2784b45
feat: fetching c_type representation from IntrinsicType's hashmap dir…
madhav-madhusoodanan Jun 17, 2025
d0f2d78
feat: changed from TypeKind::Int(bool) to TypeKind::Int(Sign) for more
madhav-madhusoodanan Jun 19, 2025
758d7ec
Feat: setup load function for x86 intrinsics
madhav-madhusoodanan Jun 24, 2025
6e190b2
feat: implemented c_single_vector_type and fixed logical errors in
madhav-madhusoodanan Jun 24, 2025
e947072
feat: added vector types to support intrinsics that does not represent a
madhav-madhusoodanan Jul 4, 2025
e57c4bf
feat: added memsize and rust_type implementation
madhav-madhusoodanan Jul 4, 2025
5b89089
feat: added initial functionality for building Rust files
madhav-madhusoodanan Jul 10, 2025
2624722
Added output comparison functionality
madhav-madhusoodanan Jul 10, 2025
9cd1cb0
feat: moved to_range to to_vector for extended iteration capabilities
madhav-madhusoodanan Jul 12, 2025
20b5d3b
Added constraint mapping
madhav-madhusoodanan Jul 13, 2025
661bf30
feat: added x86 C compilation and CI pipeline for testing x86 intrinsics
madhav-madhusoodanan Jul 19, 2025
cc33f60
Merge branch 'master' into x86_extension_intrinsic_test
madhav-madhusoodanan Jul 19, 2025
1d89c29
fix: update CI to avoid override issues
madhav-madhusoodanan Jul 19, 2025
a321ab4
Support generate_files option to skip file compilation and execution
madhav-madhusoodanan Jul 19, 2025
b705435
Fixed merge conflicts with master
madhav-madhusoodanan Jul 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions ci/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ fi
# Test targets compiled with extra features.
case ${TARGET} in
x86_64-unknown-linux-gnu)
TEST_CPPFLAGS="-fuse-ld=lld -I/usr/include/x86_64-linux-gnu/"
TEST_CXX_COMPILER="clang++-19"
TEST_RUNNER="${CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER}"
export STDARCH_DISABLE_ASSERT_INSTR=1

export RUSTFLAGS="${RUSTFLAGS} -C target-feature=+avx"
Expand Down Expand Up @@ -189,6 +192,14 @@ case "${TARGET}" in
--linker "${CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER}" \
--cxx-toolchain-dir "${AARCH64_BE_TOOLCHAIN}"
;;
x86_64-unknown-linux-gnu*)
CPPFLAGS="${TEST_CPPFLAGS}" RUSTFLAGS="${HOST_RUSTFLAGS}" RUST_LOG=warn \
cargo run "${INTRINSIC_TEST}" "${PROFILE}" \
--bin intrinsic-test -- intrinsics_data/x86-intel.xml \
--runner "${TEST_RUNNER}" \
--cppcompiler "${TEST_CXX_COMPILER}" \
--target "${TARGET}"
;;
*)
;;
esac
Expand Down
2 changes: 2 additions & 0 deletions crates/intrinsic-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ pretty_env_logger = "0.5.0"
rayon = "1.5.0"
diff = "0.1.12"
itertools = "0.14.0"
quick-xml = { version = "0.37.5", features = ["serialize", "overlapped-lists"] }
serde-xml-rs = "0.8.0"
12 changes: 12 additions & 0 deletions crates/intrinsic-test/src/arm/argument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::arm::intrinsic::ArmIntrinsicType;
use crate::common::argument::Argument;

impl Argument<ArmIntrinsicType> {
pub fn type_and_name_from_c(arg: &str) -> (&str, &str) {
let split_index = arg
.rfind([' ', '*'])
.expect("Couldn't split type and argname");

(arg[..split_index + 1].trim_end(), &arg[split_index + 1..])
}
}
20 changes: 13 additions & 7 deletions crates/intrinsic-test/src/arm/json_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,26 @@ fn json_to_intrinsic(
) -> Result<Intrinsic<ArmIntrinsicType>, Box<dyn std::error::Error>> {
let name = intr.name.replace(['[', ']'], "");

let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?;

let mut results = ArmIntrinsicType::from_c(&intr.return_type.value)?;
results.set_metadata("target".to_string(), target.to_string());
let args = intr
.arguments
.into_iter()
.enumerate()
.map(|(i, arg)| {
let arg_name = Argument::<ArmIntrinsicType>::type_and_name_from_c(&arg).1;
let metadata = intr.args_prep.as_mut();
let metadata = metadata.and_then(|a| a.remove(arg_name));
let arg_prep: Option<ArgPrep> = metadata.and_then(|a| a.try_into().ok());
let (type_name, arg_name) = Argument::<ArmIntrinsicType>::type_and_name_from_c(&arg);
let ty = ArmIntrinsicType::from_c(type_name)
.unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'"));

let arg_prep = intr.args_prep.as_mut();
let arg_prep = arg_prep.and_then(|a| a.remove(arg_name));
let arg_prep: Option<ArgPrep> = arg_prep.and_then(|a| a.try_into().ok());
let constraint: Option<Constraint> = arg_prep.and_then(|a| a.try_into().ok());

let mut arg = Argument::<ArmIntrinsicType>::from_c(i, &arg, target, constraint);
let mut arg =
Argument::<ArmIntrinsicType>::new(i, arg_name.to_string(), ty, constraint);
arg.ty
.set_metadata("target".to_string(), target.to_string());

// The JSON doesn't list immediates as const
let IntrinsicType {
Expand Down
64 changes: 34 additions & 30 deletions crates/intrinsic-test/src/arm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
mod argument;
mod compile;
mod config;
mod intrinsic;
mod json_parser;
mod types;

use std::fs::File;
use std::fs;

use rayon::prelude::*;

use crate::arm::config::POLY128_OSTREAM_DEF;
use crate::common::SupportedArchitectureTest;
use crate::common::cli::ProcessedCli;
use crate::common::compare::compare_outputs;
use crate::common::gen_c::{write_main_cpp, write_mod_cpp};
use crate::common::gen_rust::compile_rust_programs;
use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
use crate::common::intrinsic_helpers::TypeKind;
use crate::common::write_file::write_rust_testfiles;
use crate::common::{SupportedArchitectureTest, chunk_info};
use config::{AARCH_CONFIGURATIONS, F16_FORMATTING_DEF, build_notices};
use intrinsic::ArmIntrinsicType;
use json_parser::get_neon_intrinsics;
Expand All @@ -26,13 +27,6 @@ pub struct ArmArchitectureTest {
cli_options: ProcessedCli,
}

fn chunk_info(intrinsic_count: usize) -> (usize, usize) {
let available_parallelism = std::thread::available_parallelism().unwrap().get();
let chunk_size = intrinsic_count.div_ceil(Ord::min(available_parallelism, intrinsic_count));

(chunk_size, intrinsic_count.div_ceil(chunk_size))
}

impl SupportedArchitectureTest for ArmArchitectureTest {
fn create(cli_options: ProcessedCli) -> Box<Self> {
let a32 = cli_options.target.contains("v7");
Expand Down Expand Up @@ -68,51 +62,61 @@ impl SupportedArchitectureTest for ArmArchitectureTest {

let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len());

let cpp_compiler = compile::build_cpp_compilation(&self.cli_options).unwrap();
let cpp_compiler = compile::build_cpp_compilation(&self.cli_options);

match fs::exists("c_programs") {
Ok(false) => fs::create_dir("c_programs").unwrap(),
Ok(true) => {}
_ => return false,
}
Comment on lines +67 to +71
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe create_dir_all has the semantics that you want here (just return succes if the path already exists)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, I noticed your comments only after I pushed some updates.
I'll fix this, thank you for pointing out.


let notice = &build_notices("// ");
self.intrinsics
.par_chunks(chunk_size)
.enumerate()
.map(|(i, chunk)| {
let c_filename = format!("c_programs/mod_{i}.cpp");
let mut file = File::create(&c_filename).unwrap();
let mut file = fs::File::create(&c_filename).unwrap();
write_mod_cpp(&mut file, notice, c_target, platform_headers, chunk).unwrap();

// compile this cpp file into a .o file
let output = cpp_compiler
.compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?;
assert!(output.status.success(), "{output:?}");

if let Some(compiler) = cpp_compiler.as_ref() {
let output = compiler
.compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?;
assert!(output.status.success(), "{output:?}");
}
Ok(())
})
.collect::<Result<(), std::io::Error>>()
.unwrap();

let mut file = File::create("c_programs/main.cpp").unwrap();
let mut file = fs::File::create("c_programs/main.cpp").unwrap();
write_main_cpp(
&mut file,
c_target,
POLY128_OSTREAM_DEF,
Vec::from(platform_headers),
self.intrinsics.iter().map(|i| i.name.as_str()),
)
.unwrap();

// compile this cpp file into a .o file
info!("compiling main.cpp");
let output = cpp_compiler
.compile_object_file("main.cpp", "intrinsic-test-programs.o")
.unwrap();
assert!(output.status.success(), "{output:?}");

let object_files = (0..chunk_count)
.map(|i| format!("mod_{i}.o"))
.chain(["intrinsic-test-programs.o".to_owned()]);

let output = cpp_compiler
.link_executable(object_files, "intrinsic-test-programs")
.unwrap();
assert!(output.status.success(), "{output:?}");
if let Some(compiler) = cpp_compiler.as_ref() {
info!("compiling main.cpp");
let output = compiler
.compile_object_file("main.cpp", "intrinsic-test-programs.o")
.unwrap();
assert!(output.status.success(), "{output:?}");

let object_files = (0..chunk_count)
.map(|i| format!("mod_{i}.o"))
.chain(["intrinsic-test-programs.o".to_owned()]);

let output = compiler
.link_executable(object_files, "intrinsic-test-programs")
.unwrap();
assert!(output.status.success(), "{output:?}");
}

true
}
Expand Down
44 changes: 23 additions & 21 deletions crates/intrinsic-test/src/arm/types.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
use std::collections::HashMap;

use super::intrinsic::ArmIntrinsicType;
use crate::common::cli::Language;
use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind};

impl IntrinsicTypeDefinition for ArmIntrinsicType {
/// Gets a string containing the typename for this type in C format.
/// This assumes that the metadata hashmap contains this value at the
/// "type" key
fn c_type(&self) -> String {
let prefix = self.0.kind.c_prefix();
let const_prefix = if self.0.constant { "const " } else { "" };

if let (Some(bit_len), simd_len, vec_len) =
(self.0.bit_len, self.0.simd_len, self.0.vec_len)
{
match (simd_len, vec_len) {
(None, None) => format!("{const_prefix}{prefix}{bit_len}_t"),
(Some(simd), None) => format!("{prefix}{bit_len}x{simd}_t"),
(Some(simd), Some(vec)) => format!("{prefix}{bit_len}x{simd}x{vec}_t"),
(None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
}
} else {
todo!("{:#?}", self)
}
self.metadata
.get("type")
.expect("Failed to extract the C typename in Aarch!")
.replace("*", "")
.replace(" ", "")
.trim()
.to_string()
}

fn c_single_vector_type(&self) -> String {
Expand Down Expand Up @@ -59,7 +55,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
bit_len: Some(bl),
simd_len,
vec_len,
target,
metadata,
..
} = &self.0
{
Expand All @@ -69,7 +65,11 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
""
};

let choose_workaround = language == Language::C && target.contains("v7");
let choose_workaround = language == Language::C
&& metadata
.get("target")
.filter(|value| value.contains("v7"))
.is_some();
format!(
"vld{len}{quad}_{type}{size}",
type = match k {
Expand Down Expand Up @@ -121,15 +121,17 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
}
}

fn from_c(s: &str, target: &str) -> Result<Self, String> {
fn from_c(s: &str) -> Result<Self, String> {
const CONST_STR: &str = "const";
let mut metadata: HashMap<String, String> = HashMap::new();
metadata.insert("type".to_string(), s.to_string());
if let Some(s) = s.strip_suffix('*') {
let (s, constant) = match s.trim().strip_suffix(CONST_STR) {
Some(stripped) => (stripped, true),
None => (s, false),
};
let s = s.trim_end();
let temp_return = ArmIntrinsicType::from_c(s, target);
let temp_return = ArmIntrinsicType::from_c(s);
temp_return.map(|mut op| {
op.ptr = true;
op.ptr_constant = constant;
Expand Down Expand Up @@ -170,7 +172,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
bit_len: Some(bit_len),
simd_len,
vec_len,
target: target.to_string(),
metadata,
}))
} else {
let kind = start.parse::<TypeKind>()?;
Expand All @@ -186,7 +188,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
bit_len,
simd_len: None,
vec_len: None,
target: target.to_string(),
metadata,
}))
}
}
Expand Down
Loading
Loading