diff --git a/Cargo.lock b/Cargo.lock index 80f424dfdd..ef24a790b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,20 +73,20 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "cc" -version = "1.2.26" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "shlex", ] @@ -128,7 +128,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.4", @@ -354,7 +354,6 @@ dependencies = [ "csv", "diff", "itertools", - "lazy_static", "log", "pretty_env_logger", "rayon", @@ -403,9 +402,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linked-hash-map" @@ -624,7 +623,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -685,7 +684,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -742,7 +741,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -780,9 +779,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.102" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -838,7 +837,7 @@ version = "0.113.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "286049849b5a5bd09a8773171be96824afabffc7cc3df6caaf33a38db6cd07ae" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "semver", ] @@ -945,20 +944,20 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] diff --git a/Cargo.toml b/Cargo.toml index 0db26f31a2..a2a7b57e98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ members = [ "examples", ] exclude = [ - "crates/wasm-assert-instr-tests" + "crates/wasm-assert-instr-tests", + "rust_programs" ] [profile.release] diff --git a/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile b/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile index 74f770556d..3eba02766b 100644 --- a/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile +++ b/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile @@ -24,7 +24,7 @@ RUN mkdir /toolchains && mv "./${TOOLCHAIN}" /toolchains ENV AARCH64_BE_TOOLCHAIN="/toolchains/${TOOLCHAIN}" ENV AARCH64_BE_LIBC="${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc" -ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-gcc" +ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-g++" ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64_be -cpu max -L ${AARCH64_BE_LIBC}" ENV OBJDUMP="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-objdump" ENV STDARCH_TEST_SKIP_FEATURE=tme diff --git a/ci/run.sh b/ci/run.sh index 8eadb9285c..42c7824c9e 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -169,7 +169,7 @@ esac # Arm specific case "${TARGET}" in aarch64-unknown-linux-gnu*|armv7-unknown-linux-gnueabihf*) - CPPFLAGS="${TEST_CPPFLAGS}" RUSTFLAGS="${HOST_RUSTFLAGS}" RUST_LOG=warn \ + CPPFLAGS="${TEST_CPPFLAGS}" RUSTFLAGS="${HOST_RUSTFLAGS}" RUST_LOG=trace \ cargo run "${INTRINSIC_TEST}" "${PROFILE}" \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ --runner "${TEST_RUNNER}" \ @@ -179,7 +179,7 @@ case "${TARGET}" in ;; aarch64_be-unknown-linux-gnu*) - CPPFLAGS="${TEST_CPPFLAGS}" RUSTFLAGS="${HOST_RUSTFLAGS}" RUST_LOG=warn \ + CPPFLAGS="${TEST_CPPFLAGS}" RUSTFLAGS="${HOST_RUSTFLAGS}" RUST_LOG=trace \ cargo run "${INTRINSIC_TEST}" "${PROFILE}" \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ --runner "${TEST_RUNNER}" \ diff --git a/crates/intrinsic-test/Cargo.toml b/crates/intrinsic-test/Cargo.toml index 06051abc8d..2b2df2dacc 100644 --- a/crates/intrinsic-test/Cargo.toml +++ b/crates/intrinsic-test/Cargo.toml @@ -11,7 +11,6 @@ license = "MIT OR Apache-2.0" edition = "2024" [dependencies] -lazy_static = "1.4.0" serde = { version = "1", features = ["derive"] } serde_json = "1.0" csv = "1.1" diff --git a/crates/intrinsic-test/src/arm/compile.rs b/crates/intrinsic-test/src/arm/compile.rs index 8276cd87c1..79f8d337c3 100644 --- a/crates/intrinsic-test/src/arm/compile.rs +++ b/crates/intrinsic-test/src/arm/compile.rs @@ -1,11 +1,11 @@ -use crate::common::compile_c::CompilationCommandBuilder; -use crate::common::gen_c::compile_c_programs; +use crate::common::compile_c::{CompilationCommand, CompilationCommandBuilder}; pub fn compile_c_arm( - intrinsics_name_list: &[String], compiler: &str, target: &str, cxx_toolchain_dir: Option<&str>, + inputs: &[String], + output: Option<&str>, ) -> bool { // -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations let mut command = CompilationCommandBuilder::new() @@ -49,16 +49,54 @@ pub fn compile_c_arm( command = command.add_extra_flag("-flax-vector-conversions"); } - let compiler_commands = intrinsics_name_list - .iter() - .map(|intrinsic_name| { - command - .clone() - .set_input_name(intrinsic_name) - .set_output_name(intrinsic_name) - .make_string() - }) - .collect::>(); + let mut command = command.into_command(); + command.command_mut().current_dir("c_programs"); - compile_c_programs(&compiler_commands) + for input in inputs { + assert!( + std::path::Path::new("c_programs").join(input).exists(), + "{}", + input + ); + } + command.command_mut().args(inputs); + + if let Some(output) = output { + trace!("running {compiler} to produce {output}"); + if output.ends_with(".o") { + command.command_mut().arg("-c"); + } + command.command_mut().args(["-o", output]); + + if let CompilationCommand::CustomLinker { linker, .. } = &mut command { + linker.arg(format!("c_programs/{output}")); + } + } else { + trace!("running {compiler}"); + } + + if log::log_enabled!(log::Level::Trace) { + command.command_mut().stdout(std::process::Stdio::inherit()); + command.command_mut().stderr(std::process::Stdio::inherit()); + } + + let output = command.output(); + + trace!("{compiler} is done"); + + if let Ok(output) = output { + if output.status.success() { + true + } else { + error!( + "Failed to compile code for intrinsics: \n\nstdout:\n{}\n\nstderr:\n{}", + std::str::from_utf8(&output.stdout).unwrap_or(""), + std::str::from_utf8(&output.stderr).unwrap_or("") + ); + false + } + } else { + error!("Command failed: {output:#?}"); + false + } } diff --git a/crates/intrinsic-test/src/arm/config.rs b/crates/intrinsic-test/src/arm/config.rs index cee80374ae..53c3ba29b4 100644 --- a/crates/intrinsic-test/src/arm/config.rs +++ b/crates/intrinsic-test/src/arm/config.rs @@ -26,7 +26,7 @@ pub const F16_FORMATTING_DEF: &str = r#" /// Used to continue `Debug`ging SIMD types as `MySimd(1, 2, 3, 4)`, as they /// were before moving to array-based simd. #[inline] -fn debug_simd_finish( +pub(crate) fn debug_simd_finish( formatter: &mut core::fmt::Formatter<'_>, type_name: &str, array: &[T; N], @@ -47,11 +47,11 @@ impl core::fmt::Debug for Hex { } } -fn debug_f16(x: T) -> impl core::fmt::Debug { +pub(crate) fn debug_f16(x: T) -> impl core::fmt::Debug { Hex(x) } -trait DebugHexF16 { +pub(crate) trait DebugHexF16 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result; } @@ -114,7 +114,6 @@ pub const AARCH_CONFIGURATIONS: &str = r#" #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_dotprod))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sha3))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] #![feature(fmt_helpers_for_derive)] diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs index 6aaa49ff97..34633a8a3c 100644 --- a/crates/intrinsic-test/src/arm/mod.rs +++ b/crates/intrinsic-test/src/arm/mod.rs @@ -1,17 +1,21 @@ +use std::fs::File; + +use rayon::prelude::*; + mod compile; mod config; mod intrinsic; mod json_parser; mod types; +use crate::arm::compile::compile_c_arm; use crate::common::SupportedArchitectureTest; use crate::common::cli::ProcessedCli; use crate::common::compare::compare_outputs; -use crate::common::gen_rust::compile_rust_programs; -use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; +use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; +use crate::common::gen_rust::{compile_rust_programs, write_cargo_toml, write_main_rs}; +use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; -use crate::common::write_file::{write_c_testfiles, write_rust_testfiles}; -use compile::compile_c_arm; use config::{AARCH_CONFIGURATIONS, F16_FORMATTING_DEF, POLY128_OSTREAM_DEF, build_notices}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -51,60 +55,160 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } fn build_c_file(&self) -> bool { - let compiler = self.cli_options.cpp_compiler.as_deref(); + let compiler = self.cli_options.cpp_compiler.as_deref().unwrap(); let target = &self.cli_options.target; let cxx_toolchain_dir = self.cli_options.cxx_toolchain_dir.as_deref(); let c_target = "aarch64"; - let intrinsics_name_list = write_c_testfiles( - &self - .intrinsics - .iter() - .map(|i| i as &dyn IntrinsicDefinition<_>) - .collect::>(), - target, + let available_parallelism = std::thread::available_parallelism().unwrap().get(); + let chunk_size = self.intrinsics.len().div_ceil(available_parallelism); + + 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(); + write_mod_cpp(&mut file, notice, c_target, chunk).unwrap(); + + // compile this cpp file into a .o file + + compile_c_arm( + compiler, + target, + cxx_toolchain_dir, + &[format!("mod_{i}.cpp")], + Some(&format!("mod_{i}.o")), + ); + + Ok(()) + }) + .collect::>() + .unwrap(); + + let mut file = File::create("c_programs/main.cpp").unwrap(); + write_main_cpp( + &mut file, c_target, - &["arm_neon.h", "arm_acle.h", "arm_fp16.h"], - &build_notices("// "), - &[POLY128_OSTREAM_DEF], - ); + POLY128_OSTREAM_DEF, + self.intrinsics.iter().map(|i| i.name.as_str()), + ) + .unwrap(); - match compiler { - None => true, - Some(compiler) => compile_c_arm( - intrinsics_name_list.as_slice(), + if let Some(linker) = &self.cli_options.linker { + compile_c_arm( compiler, target, cxx_toolchain_dir, - ), + &["main.cpp".to_string()], + Some("intrinsic-test-programs.o"), + ); + + let mut cmd = std::process::Command::new(linker); + cmd.current_dir("c_programs"); + + let mut inputs = vec![]; + for i in 0..Ord::min(available_parallelism, self.intrinsics.len()) { + inputs.push(format!("mod_{i}.o")); + } + cmd.args(inputs); + + cmd.arg("intrinsic-test-programs.o"); + + cmd.arg("-o"); + cmd.arg("intrinsic-test-programs"); + + if log::log_enabled!(log::Level::Trace) { + cmd.stdout(std::process::Stdio::inherit()); + cmd.stderr(std::process::Stdio::inherit()); + } + + assert!(cmd.output().unwrap().status.success()); + } else { + let mut inputs = vec![format!("main.cpp")]; + for i in 0..Ord::min(available_parallelism, self.intrinsics.len()) { + inputs.push(format!("mod_{i}.o")); + } + + compile_c_arm( + compiler, + target, + cxx_toolchain_dir, + &inputs, + Some("intrinsic-test-programs"), + ); } + + true } fn build_rust_file(&self) -> bool { - let rust_target = if self.cli_options.target.contains("v7") { + std::fs::create_dir_all("rust_programs/src").unwrap(); + + let architecture = if self.cli_options.target.contains("v7") { "arm" } else { "aarch64" }; + + let available_parallelism = std::thread::available_parallelism().unwrap().get(); + let chunk_size = self.intrinsics.len().div_ceil(available_parallelism); + + let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); + write_cargo_toml(&mut cargo, &[]).unwrap(); + + let mut main_rs = File::create("rust_programs/src/main.rs").unwrap(); + write_main_rs( + &mut main_rs, + available_parallelism, + architecture, + AARCH_CONFIGURATIONS, + F16_FORMATTING_DEF, + self.intrinsics.iter().map(|i| i.name.as_str()), + ) + .unwrap(); + let target = &self.cli_options.target; let toolchain = self.cli_options.toolchain.as_deref(); let linker = self.cli_options.linker.as_deref(); - let intrinsics_name_list = write_rust_testfiles( - self.intrinsics - .iter() - .map(|i| i as &dyn IntrinsicDefinition<_>) - .collect::>(), - rust_target, - &build_notices("// "), - F16_FORMATTING_DEF, - AARCH_CONFIGURATIONS, + + warn!( + "available parallelism: {:?} {}", + std::thread::available_parallelism(), + rayon::current_num_threads(), ); - compile_rust_programs(intrinsics_name_list, toolchain, target, linker) + let notice = &build_notices("// "); + self.intrinsics + .par_chunks(chunk_size) + .enumerate() + .map(|(i, chunk)| { + use std::io::Write; + + let rust_filename = format!("rust_programs/src/mod_{i}.rs"); + trace!("generating `{rust_filename}`"); + let mut file = File::create(rust_filename).unwrap(); + + write!(file, "{notice}")?; + + writeln!(file, "use core_arch::arch::{architecture}::*;")?; + writeln!(file, "use crate::{{debug_simd_finish, debug_f16}};")?; + + for intrinsic in chunk { + crate::common::gen_rust::create_rust_test_module(&mut file, intrinsic)?; + } + + Ok(()) + }) + .collect::>() + .unwrap(); + + compile_rust_programs(toolchain, target, linker) } fn compare_outputs(&self) -> bool { - if let Some(ref toolchain) = self.cli_options.toolchain { + if self.cli_options.toolchain.is_some() { let intrinsics_name_list = self .intrinsics .iter() @@ -113,8 +217,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { compare_outputs( &intrinsics_name_list, - toolchain, - &self.cli_options.c_runner, + &self.cli_options.runner, &self.cli_options.target, ) } else { diff --git a/crates/intrinsic-test/src/common/argument.rs b/crates/intrinsic-test/src/common/argument.rs index 443ccb919f..b72c954f4a 100644 --- a/crates/intrinsic-test/src/common/argument.rs +++ b/crates/intrinsic-test/src/common/argument.rs @@ -125,38 +125,46 @@ where /// Creates a line for each argument that initializes an array for C from which `loads` argument /// values can be loaded as a sliding window. /// e.g `const int32x2_t a_vals = {0x3effffff, 0x3effffff, 0x3f7fffff}`, if loads=2. - pub fn gen_arglists_c(&self, indentation: Indentation, loads: u32) -> String { - self.iter() - .filter(|&arg| !arg.has_constraint()) - .map(|arg| { - format!( - "{indentation}const {ty} {name}_vals[] = {values};", - ty = arg.ty.c_scalar_type(), - name = arg.name, - values = arg.ty.populate_random(indentation, loads, &Language::C) - ) - }) - .collect::>() - .join("\n") + pub fn gen_arglists_c( + &self, + w: &mut impl std::io::Write, + indentation: Indentation, + loads: u32, + ) -> std::io::Result<()> { + for arg in self.iter().filter(|&arg| !arg.has_constraint()) { + writeln!( + w, + "{indentation}const {ty} {name}_vals[] = {values};", + ty = arg.ty.c_scalar_type(), + name = arg.name, + values = arg.ty.populate_random(indentation, loads, &Language::C) + )? + } + + Ok(()) } /// Creates a line for each argument that initializes an array for Rust from which `loads` argument /// values can be loaded as a sliding window, e.g `const A_VALS: [u32; 20] = [...];` - pub fn gen_arglists_rust(&self, indentation: Indentation, loads: u32) -> String { - self.iter() - .filter(|&arg| !arg.has_constraint()) - .map(|arg| { - format!( - "{indentation}{bind} {name}: [{ty}; {load_size}] = {values};", - bind = arg.rust_vals_array_binding(), - name = arg.rust_vals_array_name(), - ty = arg.ty.rust_scalar_type(), - load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1, - values = arg.ty.populate_random(indentation, loads, &Language::Rust) - ) - }) - .collect::>() - .join("\n") + pub fn gen_arglists_rust( + &self, + w: &mut impl std::io::Write, + indentation: Indentation, + loads: u32, + ) -> std::io::Result<()> { + for arg in self.iter().filter(|&arg| !arg.has_constraint()) { + writeln!( + w, + "{indentation}{bind} {name}: [{ty}; {load_size}] = {values};", + bind = arg.rust_vals_array_binding(), + name = arg.rust_vals_array_name(), + ty = arg.ty.rust_scalar_type(), + load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1, + values = arg.ty.populate_random(indentation, loads, &Language::Rust) + )? + } + + Ok(()) } /// Creates a line for each argument that initializes the argument from an array `[arg]_vals` at diff --git a/crates/intrinsic-test/src/common/cli.rs b/crates/intrinsic-test/src/common/cli.rs index 1d57272300..f50034ebf8 100644 --- a/crates/intrinsic-test/src/common/cli.rs +++ b/crates/intrinsic-test/src/common/cli.rs @@ -60,7 +60,7 @@ pub struct ProcessedCli { pub filename: PathBuf, pub toolchain: Option, pub cpp_compiler: Option, - pub c_runner: String, + pub runner: String, pub target: String, pub linker: Option, pub cxx_toolchain_dir: Option, @@ -90,11 +90,10 @@ impl ProcessedCli { (None, None) } else { ( - Some( - cli_options - .toolchain - .map_or_else(String::new, |t| format!("+{t}")), - ), + match cli_options.toolchain { + Some(t) => Some(format!("+{t}")), + None => Some(String::new()), // NOTE this is confusing + }, Some(cli_options.cppcompiler), ) }; @@ -102,7 +101,7 @@ impl ProcessedCli { Self { toolchain, cpp_compiler, - c_runner, + runner: c_runner, target, linker, cxx_toolchain_dir, diff --git a/crates/intrinsic-test/src/common/compare.rs b/crates/intrinsic-test/src/common/compare.rs index 9e0cbe8cd6..4099d7420d 100644 --- a/crates/intrinsic-test/src/common/compare.rs +++ b/crates/intrinsic-test/src/common/compare.rs @@ -2,27 +2,27 @@ use super::cli::FailureReason; use rayon::prelude::*; use std::process::Command; -pub fn compare_outputs( - intrinsic_name_list: &Vec, - toolchain: &str, - runner: &str, - target: &str, -) -> bool { +pub fn compare_outputs(intrinsic_name_list: &Vec, runner: &str, target: &str) -> bool { + fn runner_command(runner: &str) -> Command { + let mut it = runner.split_whitespace(); + let mut cmd = Command::new(it.next().unwrap()); + cmd.args(it); + + cmd + } + let intrinsics = intrinsic_name_list .par_iter() .filter_map(|intrinsic_name| { - let c = Command::new("sh") - .arg("-c") - .arg(format!("{runner} ./c_programs/{intrinsic_name}")) + let c = runner_command(runner) + .arg("intrinsic-test-programs") + .arg(intrinsic_name) + .current_dir("c_programs") .output(); - let rust = Command::new("sh") - .current_dir("rust_programs") - .arg("-c") - .arg(format!( - "cargo {toolchain} run --target {target} --bin {intrinsic_name} --release", - )) - .env("RUSTFLAGS", "-Cdebuginfo=0") + let rust = runner_command(runner) + .arg(format!("target/{target}/release/intrinsic-test-programs")) + .arg(intrinsic_name) .output(); let (c, rust) = match (c, rust) { @@ -32,19 +32,19 @@ pub fn compare_outputs( if !c.status.success() { error!( - "Failed to run C program for intrinsic {intrinsic_name}\nstdout: {stdout}\nstderr: {stderr}", + "Failed to run C program for intrinsic `{intrinsic_name}`\nstdout: {stdout}\nstderr: {stderr}", stdout = std::str::from_utf8(&c.stdout).unwrap_or(""), stderr = std::str::from_utf8(&c.stderr).unwrap_or(""), - ); + ); return Some(FailureReason::RunC(intrinsic_name.clone())); } if !rust.status.success() { error!( - "Failed to run Rust program for intrinsic {intrinsic_name}\nstdout: {stdout}\nstderr: {stderr}", - stdout = std::str::from_utf8(&rust.stdout).unwrap_or(""), - stderr = std::str::from_utf8(&rust.stderr).unwrap_or(""), - ); + "Failed to run Rust program for intrinsic `{intrinsic_name}`\nstdout: {stdout}\nstderr: {stderr}", + stdout = String::from_utf8_lossy(&rust.stdout), + stderr = String::from_utf8_lossy(&rust.stderr), + ); return Some(FailureReason::RunRust(intrinsic_name.clone())); } diff --git a/crates/intrinsic-test/src/common/compile_c.rs b/crates/intrinsic-test/src/common/compile_c.rs index aebb7b111e..dfb18693b3 100644 --- a/crates/intrinsic-test/src/common/compile_c.rs +++ b/crates/intrinsic-test/src/common/compile_c.rs @@ -7,8 +7,6 @@ pub struct CompilationCommandBuilder { optimization: String, include_paths: Vec, project_root: Option, - output: String, - input: String, linker: Option, extra_flags: Vec, } @@ -23,8 +21,6 @@ impl CompilationCommandBuilder { optimization: "2".to_string(), include_paths: Vec::new(), project_root: None, - output: String::new(), - input: String::new(), linker: None, extra_flags: Vec::new(), } @@ -71,18 +67,6 @@ impl CompilationCommandBuilder { self } - /// The name of the output executable, without any suffixes - pub fn set_output_name(mut self, path: &str) -> Self { - self.output = path.to_string(); - self - } - - /// The name of the input C file, without any suffixes - pub fn set_input_name(mut self, path: &str) -> Self { - self.input = path.to_string(); - self - } - pub fn set_linker(mut self, linker: String) -> Self { self.linker = Some(linker); self @@ -99,56 +83,98 @@ impl CompilationCommandBuilder { } } +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum CompilationCommand { + Simple(std::process::Command), + #[allow(unused)] + CustomLinker { + cmd: std::process::Command, + linker: std::process::Command, + }, +} + +impl CompilationCommand { + pub fn command_mut(&mut self) -> &mut std::process::Command { + match self { + CompilationCommand::Simple(command) => command, + CompilationCommand::CustomLinker { cmd, .. } => cmd, + } + } + + pub fn output(self) -> std::io::Result { + match self { + CompilationCommand::Simple(mut cmd) => cmd.output(), + CompilationCommand::CustomLinker { + mut cmd, + mut linker, + } => { + let output = cmd.output()?; + + if log::log_enabled!(log::Level::Trace) { + linker.stdout(std::process::Stdio::inherit()); + linker.stderr(std::process::Stdio::inherit()); + } + + if let Err(e) = linker.output() { + panic!( + "Failed running custom linker {:?}:\n{e:?}", + linker.get_program(), + ); + } + // if cleanup.exists() { + // std::fs::remove_file(cleanup)?; + // } + + Ok(output) + } + } + } +} + impl CompilationCommandBuilder { - pub fn make_string(self) -> String { - let arch_flags = self.arch_flags.join("+"); + pub fn into_command(self) -> CompilationCommand { + // let project_root = self.project_root.unwrap_or_default(); + // let project_root_str = project_root.as_str(); + + let mut cmd = std::process::Command::new(self.compiler); + let flags = std::env::var("CPPFLAGS").unwrap_or("".into()); - let project_root = self.project_root.unwrap_or_default(); - let project_root_str = project_root.as_str(); - let mut output = self.output.clone(); - if self.linker.is_some() { - output += ".o" - }; - let mut command = format!( - "{} {flags} -march={arch_flags} \ - -O{} \ - -o {project_root}/{} \ - {project_root}/{}.cpp", - self.compiler, self.optimization, output, self.input, - ); - - command = command + " " + self.extra_flags.join(" ").as_str(); + cmd.args(flags.split_whitespace()); + + cmd.arg(format!("-march={}", self.arch_flags.join("+"))); + + cmd.arg(format!("-O{}", self.optimization)); + + cmd.args(self.extra_flags); if let Some(target) = &self.target { - command = command + " --target=" + target; + cmd.arg(format!("--target={target}")); } - if let (Some(linker), Some(cxx_toolchain_dir)) = (&self.linker, &self.cxx_toolchain_dir) { - let include_args = self - .include_paths - .iter() - .map(|path| "--include-directory=".to_string() + cxx_toolchain_dir + path) - .collect::>() - .join(" "); - - command = command - + " -c " - + include_args.as_str() - + " && " - + linker - + " " - + project_root_str - + "/" - + &output - + " -o " - + project_root_str - + "/" - + &self.output - + " && rm " - + project_root_str - + "/" - + &output; + if let (Some(_linker), Some(cxx_toolchain_dir)) = (&self.linker, &self.cxx_toolchain_dir) { + cmd.arg("-c"); + cmd.args( + self.include_paths + .iter() + .map(|path| "--include-directory=".to_string() + cxx_toolchain_dir + path), + ); + + // let linker_cmd = std::process::Command::new(linker); + // linker_cmd.arg(format!("{project_root_str}/{output}")); + + // let remove_path = PathBuf::new(); + // PathBuf::from(format!("{project_root_str}/{output}")); + + // CompilationCommand::CustomLinker { + // cmd, + // linker: linker_cmd, + // // cleanup: remove_path, + // } + + CompilationCommand::Simple(cmd) + } else { + CompilationCommand::Simple(cmd) } - command } } diff --git a/crates/intrinsic-test/src/common/gen_c.rs b/crates/intrinsic-test/src/common/gen_c.rs index 1cfb66c39b..aa587f71b3 100644 --- a/crates/intrinsic-test/src/common/gen_c.rs +++ b/crates/intrinsic-test/src/common/gen_c.rs @@ -1,8 +1,3 @@ -use itertools::Itertools; -use rayon::prelude::*; -use std::collections::BTreeMap; -use std::process::Command; - use super::argument::Argument; use super::indentation::Indentation; use super::intrinsic::IntrinsicDefinition; @@ -11,104 +6,16 @@ use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. const PASSES: u32 = 20; -// Formats the main C program template with placeholders -pub fn format_c_main_template( - notices: &str, - header_files: &[&str], - arch_identifier: &str, - arch_specific_definitions: &[&str], - arglists: &str, - passes: &str, -) -> String { - format!( - r#"{notices}{header_files} -#include -#include -#include -#include - -template T1 cast(T2 x) {{ - static_assert(sizeof(T1) == sizeof(T2), "sizeof T1 and T2 must be the same"); - T1 ret{{}}; - memcpy(&ret, &x, sizeof(T1)); - return ret; -}} - -std::ostream& operator<<(std::ostream& os, float16_t value) {{ - uint16_t temp = 0; - memcpy(&temp, &value, sizeof(float16_t)); - std::stringstream ss; - ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << temp; - os << ss.str(); - return os; -}} - -#ifdef __{arch_identifier}__ -{arch_specific_definitions} -#endif - -{arglists} - -int main(int argc, char **argv) {{ -{passes} - return 0; -}}"#, - header_files = header_files - .iter() - .map(|header| format!("#include <{header}>")) - .collect::>() - .join("\n"), - arch_specific_definitions = arch_specific_definitions.join("\n"), - ) -} - -pub fn compile_c_programs(compiler_commands: &[String]) -> bool { - compiler_commands - .par_iter() - .map(|compiler_command| { - let output = Command::new("sh").arg("-c").arg(compiler_command).output(); - if let Ok(output) = output { - if output.status.success() { - true - } else { - error!( - "Failed to compile code for intrinsics: \n\nstdout:\n{}\n\nstderr:\n{}", - std::str::from_utf8(&output.stdout).unwrap_or(""), - std::str::from_utf8(&output.stderr).unwrap_or("") - ); - false - } - } else { - error!("Command failed: {output:#?}"); - false - } - }) - .find_any(|x| !x) - .is_none() -} - -// Creates directory structure and file path mappings -pub fn setup_c_file_paths(identifiers: &Vec) -> BTreeMap<&String, String> { - let _ = std::fs::create_dir("c_programs"); - identifiers - .par_iter() - .map(|identifier| { - let c_filename = format!(r#"c_programs/{identifier}.cpp"#); - - (identifier, c_filename) - }) - .collect::>() -} - pub fn generate_c_test_loop( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, additional: &str, passes: u32, - _target: &str, -) -> String { +) -> std::io::Result<()> { let body_indentation = indentation.nested(); - format!( + writeln!( + w, "{indentation}for (int i=0; i<{passes}; i++) {{\n\ {loaded_args}\ {body_indentation}auto __return_value = {intrinsic_call}({args});\n\ @@ -121,78 +28,171 @@ pub fn generate_c_test_loop( ) } -pub fn generate_c_constraint_blocks( +pub fn generate_c_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, - constraints: &[&Argument], + constraints: &mut (impl Iterator> + Clone), name: String, - target: &str, -) -> String { - if let Some((current, constraints)) = constraints.split_last() { - let range = current - .constraint - .iter() - .map(|c| c.to_range()) - .flat_map(|r| r.into_iter()); - - let body_indentation = indentation.nested(); - range - .map(|i| { - format!( - "{indentation}{{\n\ - {body_indentation}{ty} {name} = {val};\n\ - {pass}\n\ - {indentation}}}", - name = current.name, - ty = current.ty.c_type(), - val = i, - pass = generate_c_constraint_blocks( - intrinsic, - body_indentation, - constraints, - format!("{name}-{i}"), - target, - ) - ) - }) - .join("\n") - } else { - generate_c_test_loop(intrinsic, indentation, &name, PASSES, target) +) -> std::io::Result<()> { + let Some(current) = constraints.next() else { + return generate_c_test_loop(w, intrinsic, indentation, &name, PASSES); + }; + + let body_indentation = indentation.nested(); + for i in current.constraint.iter().flat_map(|c| c.to_range()) { + let ty = current.ty.c_type(); + + writeln!(w, "{indentation}{{")?; + writeln!(w, "{body_indentation}{ty} {} = {i};", current.name)?; + + generate_c_constraint_blocks( + w, + intrinsic, + body_indentation, + &mut constraints.clone(), + format!("{name}-{i}"), + )?; + + writeln!(w, "{indentation}}}")?; } + + Ok(()) } // Compiles C test programs using specified compiler -pub fn create_c_test_program( +pub fn create_c_test_function( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, - header_files: &[&str], - target: &str, - c_target: &str, - notices: &str, - arch_specific_definitions: &[&str], -) -> String { +) -> std::io::Result<()> { + let indentation = Indentation::default(); + + writeln!(w, "int run_{}() {{", intrinsic.name())?; + + // Define the arrays of arguments. let arguments = intrinsic.arguments(); - let constraints = arguments - .iter() - .filter(|&i| i.has_constraint()) - .collect_vec(); + arguments.gen_arglists_c(w, indentation.nested(), PASSES)?; - let indentation = Indentation::default(); - format_c_main_template( - notices, - header_files, - c_target, - arch_specific_definitions, - intrinsic - .arguments() - .gen_arglists_c(indentation, PASSES) - .as_str(), - generate_c_constraint_blocks( - intrinsic, - indentation.nested(), - constraints.as_slice(), - Default::default(), - target, - ) - .as_str(), - ) + generate_c_constraint_blocks( + w, + intrinsic, + indentation.nested(), + &mut arguments.iter().rev().filter(|&i| i.has_constraint()), + Default::default(), + )?; + + writeln!(w, " return 0;")?; + writeln!(w, "}}")?; + + Ok(()) +} + +pub fn write_mod_cpp( + w: &mut impl std::io::Write, + notice: &str, + architecture: &str, + intrinsics: &[impl IntrinsicDefinition], +) -> std::io::Result<()> { + write!(w, "{notice}")?; + + for header in ["arm_neon.h", "arm_acle.h", "arm_fp16.h"] { + writeln!(w, "#include <{header}>")?; + } + + writeln!( + w, + r#" +#include +#include +#include +#include + +template T1 cast(T2 x) {{ + static_assert(sizeof(T1) == sizeof(T2), "sizeof T1 and T2 must be the same"); + T1 ret{{}}; + memcpy(&ret, &x, sizeof(T1)); + return ret; +}} + +std::ostream& operator<<(std::ostream& os, float16_t value); + + + +"# + )?; + + writeln!(w, "#ifdef __{architecture}__")?; + writeln!( + w, + "std::ostream& operator<<(std::ostream& os, poly128_t value);" + )?; + writeln!(w, "#endif")?; + + for intrinsic in intrinsics { + create_c_test_function(w, intrinsic)?; + } + + Ok(()) +} + +pub fn write_main_cpp<'a>( + w: &mut impl std::io::Write, + architecture: &str, + arch_specific_definitions: &str, + intrinsics: impl Iterator + Clone, +) -> std::io::Result<()> { + writeln!(w, "#include ")?; + writeln!(w, "#include ")?; + + for header in ["arm_neon.h", "arm_acle.h", "arm_fp16.h"] { + writeln!(w, "#include <{header}>")?; + } + + writeln!( + w, + r#" +#include +#include +#include + +std::ostream& operator<<(std::ostream& os, float16_t value) {{ + uint16_t temp = 0; + memcpy(&temp, &value, sizeof(float16_t)); + std::stringstream ss; + ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << temp; + os << ss.str(); + return os; +}} +"# + )?; + + writeln!(w, "#ifdef __{architecture}__")?; + writeln!(w, "{arch_specific_definitions }")?; + writeln!(w, "#endif")?; + + for intrinsic in intrinsics.clone() { + writeln!(w, "extern int run_{intrinsic}(void);")?; + } + + writeln!(w, "int main(int argc, char **argv) {{")?; + writeln!(w, " std::string intrinsic_name = argv[1];")?; + + writeln!(w, " if (false) {{")?; + + for intrinsic in intrinsics { + writeln!(w, " }} else if (intrinsic_name == \"{intrinsic}\") {{")?; + writeln!(w, " return run_{intrinsic}();")?; + } + + writeln!(w, " }} else {{")?; + writeln!( + w, + " std::cerr << \"Unknown command: \" << intrinsic_name << \"\\n\";" + )?; + writeln!(w, " return -1;")?; + writeln!(w, " }}")?; + + writeln!(w, "}}")?; + + Ok(()) } diff --git a/crates/intrinsic-test/src/common/gen_rust.rs b/crates/intrinsic-test/src/common/gen_rust.rs index 52bccaf905..13107af6ad 100644 --- a/crates/intrinsic-test/src/common/gen_rust.rs +++ b/crates/intrinsic-test/src/common/gen_rust.rs @@ -1,8 +1,4 @@ use itertools::Itertools; -use rayon::prelude::*; -use std::collections::BTreeMap; -use std::fs::File; -use std::io::Write; use std::process::Command; use super::argument::Argument; @@ -13,88 +9,103 @@ use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. const PASSES: u32 = 20; -pub fn format_rust_main_template( - notices: &str, - definitions: &str, - configurations: &str, - arch_definition: &str, - arglists: &str, - passes: &str, -) -> String { - format!( - r#"{notices}#![feature(simd_ffi)] -#![feature(link_llvm_intrinsics)] -#![feature(f16)] -{configurations} -{definitions} - -use core_arch::arch::{arch_definition}::*; - -fn main() {{ -{arglists} -{passes} -}} -"#, - ) +pub fn write_cargo_toml(w: &mut impl std::io::Write, binaries: &[String]) -> std::io::Result<()> { + writeln!( + w, + concat!( + "[package]\n", + "name = \"intrinsic-test-programs\"\n", + "version = \"{version}\"\n", + "authors = [{authors}]\n", + "license = \"{license}\"\n", + "edition = \"2018\"\n", + "[workspace]\n", + "[dependencies]\n", + "core_arch = {{ path = \"../crates/core_arch\" }}", + ), + version = env!("CARGO_PKG_VERSION"), + authors = env!("CARGO_PKG_AUTHORS") + .split(":") + .format_with(", ", |author, fmt| fmt(&format_args!("\"{author}\""))), + license = env!("CARGO_PKG_LICENSE"), + )?; + + for binary in binaries { + writeln!( + w, + concat!( + "[[bin]]\n", + "name = \"{binary}\"\n", + "path = \"{binary}/main.rs\"\n", + ), + binary = binary, + )?; + } + + Ok(()) } -pub fn compile_rust_programs( - binaries: Vec, - toolchain: Option<&str>, - target: &str, - linker: Option<&str>, -) -> bool { - let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); - cargo - .write_all( - format!( - r#"[package] -name = "intrinsic-test-programs" -version = "{version}" -authors = [{authors}] -license = "{license}" -edition = "2018" -[workspace] -[dependencies] -core_arch = {{ path = "../crates/core_arch" }} -{binaries}"#, - version = env!("CARGO_PKG_VERSION"), - authors = env!("CARGO_PKG_AUTHORS") - .split(":") - .format_with(", ", |author, fmt| fmt(&format_args!("\"{author}\""))), - license = env!("CARGO_PKG_LICENSE"), - binaries = binaries - .iter() - .map(|binary| { - format!( - r#"[[bin]] -name = "{binary}" -path = "{binary}/main.rs""#, - ) - }) - .collect::>() - .join("\n") - ) - .into_bytes() - .as_slice(), - ) - .unwrap(); - - let toolchain = match toolchain { - None => return true, - Some(t) => t, - }; +pub fn write_main_rs<'a>( + w: &mut impl std::io::Write, + available_parallelism: usize, + architecture: &str, + cfg: &str, + definitions: &str, + intrinsics: impl Iterator + Clone, +) -> std::io::Result<()> { + writeln!(w, "#![feature(simd_ffi)]")?; + writeln!(w, "#![feature(f16)]")?; + writeln!(w, "#![allow(unused)]")?; + + // NOTE: on CI, cargo is + writeln!(w, "#![allow(non_upper_case_globals)]")?; + writeln!(w, "#![allow(non_camel_case_types)]")?; + writeln!(w, "#![allow(non_snake_case)]")?; + + writeln!(w, "{cfg}")?; + writeln!(w, "{definitions}")?; + + writeln!(w, "use core_arch::arch::{architecture}::*;")?; + + for module in 0..Ord::min(available_parallelism, intrinsics.clone().count()) { + writeln!(w, "mod mod_{module};")?; + writeln!(w, "use mod_{module}::*;")?; + } + + writeln!(w, "fn main() {{")?; + writeln!(w, " match std::env::args().nth(1).unwrap().as_str() {{")?; + + for binary in intrinsics { + writeln!(w, " \"{binary}\" => run_{binary}(),")?; + } + + writeln!( + w, + " other => panic!(\"unknown intrinsic `{{}}`\", other)," + )?; + + writeln!(w, " }}")?; + writeln!(w, "}}")?; + + Ok(()) +} + +pub fn compile_rust_programs(toolchain: Option<&str>, target: &str, linker: Option<&str>) -> bool { /* If there has been a linker explicitly set from the command line then * we want to set it via setting it in the RUSTFLAGS*/ - let cargo_command = format!("cargo {toolchain} build --target {target} --release"); + trace!("Building cargo command"); + + let mut cargo_command = Command::new("cargo"); + cargo_command.current_dir("rust_programs"); - let mut command = Command::new("sh"); - command - .current_dir("rust_programs") - .arg("-c") - .arg(cargo_command); + if let Some(toolchain) = toolchain + && !toolchain.is_empty() + { + cargo_command.arg(toolchain); + } + cargo_command.args(["build", "--target", target, "--release"]); let mut rust_flags = "-Cdebuginfo=0".to_string(); if let Some(linker) = linker { @@ -102,11 +113,20 @@ path = "{binary}/main.rs""#, rust_flags.push_str(linker); rust_flags.push_str(" -C link-args=-static"); - command.env("CPPFLAGS", "-fuse-ld=lld"); + cargo_command.env("CPPFLAGS", "-fuse-ld=lld"); } - command.env("RUSTFLAGS", rust_flags); - let output = command.output(); + cargo_command.env("RUSTFLAGS", rust_flags); + + trace!("running cargo"); + + if log::log_enabled!(log::Level::Trace) { + cargo_command.stdout(std::process::Stdio::inherit()); + cargo_command.stderr(std::process::Stdio::inherit()); + } + + let output = cargo_command.output(); + trace!("cargo is done"); if let Ok(output) = output { if output.status.success() { @@ -125,26 +145,13 @@ path = "{binary}/main.rs""#, } } -// Creates directory structure and file path mappings -pub fn setup_rust_file_paths(identifiers: &Vec) -> BTreeMap<&String, String> { - identifiers - .par_iter() - .map(|identifier| { - let rust_dir = format!("rust_programs/{identifier}"); - let _ = std::fs::create_dir_all(&rust_dir); - let rust_filename = format!("{rust_dir}/main.rs"); - - (identifier, rust_filename) - }) - .collect::>() -} - pub fn generate_rust_test_loop( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, additional: &str, passes: u32, -) -> String { +) -> std::io::Result<()> { let constraints = intrinsic.arguments().as_constraint_parameters_rust(); let constraints = if !constraints.is_empty() { format!("::<{constraints}>") @@ -155,7 +162,8 @@ pub fn generate_rust_test_loop( let return_value = format_f16_return_value(intrinsic); let indentation2 = indentation.nested(); let indentation3 = indentation2.nested(); - format!( + writeln!( + w, "{indentation}for i in 0..{passes} {{\n\ {indentation2}unsafe {{\n\ {loaded_args}\ @@ -170,74 +178,63 @@ pub fn generate_rust_test_loop( ) } -pub fn generate_rust_constraint_blocks( +fn generate_rust_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, indentation: Indentation, - constraints: &[&Argument], + constraints: &mut (impl Iterator> + Clone), name: String, -) -> String { - if let Some((current, constraints)) = constraints.split_last() { - let range = current - .constraint - .iter() - .map(|c| c.to_range()) - .flat_map(|r| r.into_iter()); - - let body_indentation = indentation.nested(); - range - .map(|i| { - format!( - "{indentation}{{\n\ - {body_indentation}const {name}: {ty} = {val};\n\ - {pass}\n\ - {indentation}}}", - name = current.name, - ty = current.ty.rust_type(), - val = i, - pass = generate_rust_constraint_blocks( - intrinsic, - body_indentation, - constraints, - format!("{name}-{i}") - ) - ) - }) - .join("\n") - } else { - generate_rust_test_loop(intrinsic, indentation, &name, PASSES) +) -> std::io::Result<()> { + let Some(current) = constraints.next() else { + return generate_rust_test_loop(w, intrinsic, indentation, &name, PASSES); + }; + + let body_indentation = indentation.nested(); + for i in current.constraint.iter().flat_map(|c| c.to_range()) { + let ty = current.ty.rust_type(); + + writeln!(w, "{indentation}{{")?; + + writeln!(w, "{body_indentation}const {}: {ty} = {i};", current.name)?; + + generate_rust_constraint_blocks( + w, + intrinsic, + body_indentation, + &mut constraints.clone(), + format!("{name}-{i}"), + )?; + + writeln!(w, "{indentation}}}")?; } + + Ok(()) } // Top-level function to create complete test program -pub fn create_rust_test_program( +pub fn create_rust_test_module( + w: &mut impl std::io::Write, intrinsic: &dyn IntrinsicDefinition, - target: &str, - notice: &str, - definitions: &str, - cfg: &str, -) -> String { +) -> std::io::Result<()> { + trace!("generating `{}`", intrinsic.name()); + let indentation = Indentation::default(); + + writeln!(w, "pub fn run_{}() {{", intrinsic.name())?; + + // Define the arrays of arguments. let arguments = intrinsic.arguments(); - let constraints = arguments - .iter() - .filter(|i| i.has_constraint()) - .collect_vec(); + arguments.gen_arglists_rust(w, indentation.nested(), PASSES)?; - let indentation = Indentation::default(); - format_rust_main_template( - notice, - definitions, - cfg, - target, - intrinsic - .arguments() - .gen_arglists_rust(indentation.nested(), PASSES) - .as_str(), - generate_rust_constraint_blocks( - intrinsic, - indentation.nested(), - &constraints, - Default::default(), - ) - .as_str(), - ) + // Define any const generics as `const` items, then generate the actual test loop. + generate_rust_constraint_blocks( + w, + intrinsic, + indentation.nested(), + &mut arguments.iter().rev().filter(|i| i.has_constraint()), + Default::default(), + )?; + + writeln!(w, "}}")?; + + Ok(()) } diff --git a/crates/intrinsic-test/src/common/write_file.rs b/crates/intrinsic-test/src/common/write_file.rs index 0ba3e829a6..d919534e59 100644 --- a/crates/intrinsic-test/src/common/write_file.rs +++ b/crates/intrinsic-test/src/common/write_file.rs @@ -1,66 +1,40 @@ -use super::gen_c::create_c_test_program; -use super::gen_c::setup_c_file_paths; -use super::gen_rust::{create_rust_test_program, setup_rust_file_paths}; -use super::intrinsic::IntrinsicDefinition; -use super::intrinsic_helpers::IntrinsicTypeDefinition; -use std::fs::File; -use std::io::Write; +// use std::fs::File; +// +// use super::intrinsic::IntrinsicDefinition; +// use super::intrinsic_helpers::IntrinsicTypeDefinition; +// +// use rayon::prelude::*; -pub fn write_file(filename: &String, code: String) { - let mut file = File::create(filename).unwrap(); - file.write_all(code.into_bytes().as_slice()).unwrap(); -} - -pub fn write_c_testfiles( - intrinsics: &Vec<&dyn IntrinsicDefinition>, - target: &str, - c_target: &str, - headers: &[&str], - notice: &str, - arch_specific_definitions: &[&str], -) -> Vec { - let intrinsics_name_list = intrinsics - .iter() - .map(|i| i.name().clone()) - .collect::>(); - let filename_mapping = setup_c_file_paths(&intrinsics_name_list); - - intrinsics.iter().for_each(|&i| { - let c_code = create_c_test_program( - i, - headers, - target, - c_target, - notice, - arch_specific_definitions, - ); - if let Some(filename) = filename_mapping.get(&i.name()) { - write_file(filename, c_code) - }; - }); - - intrinsics_name_list -} - -pub fn write_rust_testfiles( - intrinsics: Vec<&dyn IntrinsicDefinition>, - rust_target: &str, - notice: &str, - definitions: &str, - cfg: &str, -) -> Vec { - let intrinsics_name_list = intrinsics - .iter() - .map(|i| i.name().clone()) - .collect::>(); - let filename_mapping = setup_rust_file_paths(&intrinsics_name_list); - - intrinsics.iter().for_each(|&i| { - let rust_code = create_rust_test_program(i, rust_target, notice, definitions, cfg); - if let Some(filename) = filename_mapping.get(&i.name()) { - write_file(filename, rust_code) - } - }); - - intrinsics_name_list -} +// pub fn write_c_testfiles<'a, T, I, E>( +// intrinsics: I, +// target: &str, +// c_target: &str, +// headers: &[&str], +// notice: &str, +// arch_specific_definitions: &[&str], +// ) -> std::io::Result> +// where +// T: IntrinsicTypeDefinition + Sized + 'a, +// I: ParallelIterator, +// E: IntrinsicDefinition + 'a, +// { +// std::fs::create_dir_all("c_programs")?; +// +// intrinsics +// .map(|intrinsic| { +// let identifier = intrinsic.name().to_owned(); +// let mut file = File::create(format!("c_programs/{identifier}.cpp")).unwrap(); +// +// crate::common::gen_c::create_c_test_function( +// &mut file, +// intrinsic, +// +// +// +// +// )?; +// +// Ok(identifier) +// }) +// .collect() +// } diff --git a/crates/intrinsic-test/src/main.rs b/crates/intrinsic-test/src/main.rs index 054138a0db..d5504f13bc 100644 --- a/crates/intrinsic-test/src/main.rs +++ b/crates/intrinsic-test/src/main.rs @@ -13,6 +13,12 @@ fn main() { let args: Cli = clap::Parser::parse(); let processed_cli_options = ProcessedCli::new(args); + warn!( + "available parallelism: {:?} {}", + std::thread::available_parallelism(), + rayon::current_num_threads(), + ); + let test_environment_result: Option> = match processed_cli_options.target.as_str() { "aarch64-unknown-linux-gnu" @@ -30,12 +36,15 @@ fn main() { let test_environment = test_environment_result.unwrap(); + warn!("building C binaries"); if !test_environment.build_c_file() { std::process::exit(2); } + warn!("building Rust binaries"); if !test_environment.build_rust_file() { std::process::exit(3); } + warn!("comparing outputs"); if !test_environment.compare_outputs() { std::process::exit(1); }