From 2a5e678a840242aa6f4087a858e9a101257d8d77 Mon Sep 17 00:00:00 2001 From: Madhav Madhusoodanan Date: Sun, 11 May 2025 00:27:40 +0530 Subject: [PATCH] moved more code generation functionality to `common` --- .../crates/intrinsic-test/src/arm/compile.rs | 64 +++++ .../intrinsic-test/src/arm/functions.rs | 270 ------------------ .../crates/intrinsic-test/src/arm/mod.rs | 62 +++- .../intrinsic-test/src/common/gen_rust.rs | 2 +- .../intrinsic-test/src/common/intrinsic.rs | 136 +++++++++ .../crates/intrinsic-test/src/common/mod.rs | 1 + .../intrinsic-test/src/common/write_file.rs | 52 ++++ 7 files changed, 301 insertions(+), 286 deletions(-) create mode 100644 library/stdarch/crates/intrinsic-test/src/arm/compile.rs delete mode 100644 library/stdarch/crates/intrinsic-test/src/arm/functions.rs create mode 100644 library/stdarch/crates/intrinsic-test/src/common/write_file.rs diff --git a/library/stdarch/crates/intrinsic-test/src/arm/compile.rs b/library/stdarch/crates/intrinsic-test/src/arm/compile.rs new file mode 100644 index 000000000000..80b25aa8ab9d --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/arm/compile.rs @@ -0,0 +1,64 @@ +use crate::common::compile_c::CompilationCommandBuilder; +use crate::common::gen_c::compile_c; + +pub fn compile_c_arm( + intrinsics_name_list: &Vec, + compiler: &str, + target: &str, + cxx_toolchain_dir: Option<&str>, +) -> bool { + // -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations + let mut command = CompilationCommandBuilder::new() + .add_arch_flags(vec!["armv8.6-a", "crypto", "crc", "dotprod", "fp16"]) + .set_compiler(compiler) + .set_target(target) + .set_opt_level("2") + .set_cxx_toolchain_dir(cxx_toolchain_dir) + .set_project_root("c_programs") + .add_extra_flags(vec!["-ffp-contract=off", "-Wno-narrowing"]); + + if !target.contains("v7") { + command = command.add_arch_flags(vec!["faminmax", "lut", "sha3"]); + } + + /* + * clang++ cannot link an aarch64_be object file, so we invoke + * aarch64_be-unknown-linux-gnu's C++ linker. This ensures that we + * are testing the intrinsics against LLVM. + * + * Note: setting `--sysroot=<...>` which is the obvious thing to do + * does not work as it gets caught up with `#include_next ` + * not existing... + */ + if target.contains("aarch64_be") { + command = command + .set_linker( + cxx_toolchain_dir.unwrap_or("").to_string() + "/bin/aarch64_be-none-linux-gnu-g++", + ) + .set_include_paths(vec![ + "/include", + "/aarch64_be-none-linux-gnu/include", + "/aarch64_be-none-linux-gnu/include/c++/14.2.1", + "/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu", + "/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward", + "/aarch64_be-none-linux-gnu/libc/usr/include", + ]); + } + + if !compiler.contains("clang") { + 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) + .to_string() + }) + .collect::>(); + + compile_c(&compiler_commands) +} diff --git a/library/stdarch/crates/intrinsic-test/src/arm/functions.rs b/library/stdarch/crates/intrinsic-test/src/arm/functions.rs deleted file mode 100644 index a60a02cd596b..000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/functions.rs +++ /dev/null @@ -1,270 +0,0 @@ -use super::config::{AARCH_CONFIGURATIONS, POLY128_OSTREAM_DEF, build_notices}; -use super::intrinsic::ArmIntrinsicType; -use crate::common::argument::Argument; -use crate::common::compile_c::CompilationCommandBuilder; -use crate::common::gen_c::{compile_c, create_c_filenames, generate_c_program}; -use crate::common::gen_rust::{compile_rust, create_rust_filenames, generate_rust_program}; -use crate::common::indentation::Indentation; -use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; -use crate::common::intrinsic_helpers::IntrinsicTypeDefinition; -use crate::common::write_file; -use itertools::Itertools; -use rayon::prelude::*; - -// The number of times each intrinsic will be called. -const PASSES: u32 = 20; - -fn gen_code_c( - indentation: Indentation, - intrinsic: &Intrinsic, - constraints: &[&Argument], - 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 = gen_code_c( - body_indentation, - intrinsic, - constraints, - format!("{name}-{i}"), - target, - ) - ) - }) - .join("\n") - } else { - intrinsic.generate_loop_c(indentation, &name, PASSES, target) - } -} - -fn generate_c_program_arm( - header_files: &[&str], - intrinsic: &Intrinsic, - target: &str, -) -> String { - let constraints = intrinsic - .arguments - .iter() - .filter(|&i| i.has_constraint()) - .collect_vec(); - - let indentation = Indentation::default(); - generate_c_program( - build_notices("// ").as_str(), - header_files, - "aarch64", - &[POLY128_OSTREAM_DEF], - intrinsic - .arguments - .gen_arglists_c(indentation, PASSES) - .as_str(), - gen_code_c( - indentation.nested(), - intrinsic, - constraints.as_slice(), - Default::default(), - target, - ) - .as_str(), - ) -} - -fn gen_code_rust( - indentation: Indentation, - intrinsic: &Intrinsic, - constraints: &[&Argument], - name: String, -) -> String { - println!("{}", name); - 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 = gen_code_rust( - body_indentation, - intrinsic, - constraints, - format!("{name}-{i}") - ) - ) - }) - .join("\n") - } else { - intrinsic.generate_loop_rust(indentation, &name, PASSES) - } -} - -fn generate_rust_program_arm(intrinsic: &Intrinsic, target: &str) -> String { - let constraints = intrinsic - .arguments - .iter() - .filter(|i| i.has_constraint()) - .collect_vec(); - - let indentation = Indentation::default(); - let final_target = if target.contains("v7") { - "arm" - } else { - "aarch64" - }; - generate_rust_program( - build_notices("// ").as_str(), - AARCH_CONFIGURATIONS, - final_target, - intrinsic - .arguments - .gen_arglists_rust(indentation.nested(), PASSES) - .as_str(), - gen_code_rust( - indentation.nested(), - intrinsic, - &constraints, - Default::default(), - ) - .as_str(), - ) -} - -fn compile_c_arm( - intrinsics_name_list: &Vec, - compiler: &str, - target: &str, - cxx_toolchain_dir: Option<&str>, -) -> bool { - // -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations - let mut command = CompilationCommandBuilder::new() - .add_arch_flags(vec!["armv8.6-a", "crypto", "crc", "dotprod", "fp16"]) - .set_compiler(compiler) - .set_target(target) - .set_opt_level("2") - .set_cxx_toolchain_dir(cxx_toolchain_dir) - .set_project_root("c_programs") - .add_extra_flags(vec!["-ffp-contract=off", "-Wno-narrowing"]); - - if !target.contains("v7") { - command = command.add_arch_flags(vec!["faminmax", "lut", "sha3"]); - } - - /* - * clang++ cannot link an aarch64_be object file, so we invoke - * aarch64_be-unknown-linux-gnu's C++ linker. This ensures that we - * are testing the intrinsics against LLVM. - * - * Note: setting `--sysroot=<...>` which is the obvious thing to do - * does not work as it gets caught up with `#include_next ` - * not existing... - */ - if target.contains("aarch64_be") { - command = command - .set_linker( - cxx_toolchain_dir.unwrap_or("").to_string() + "/bin/aarch64_be-none-linux-gnu-g++", - ) - .set_include_paths(vec![ - "/include", - "/aarch64_be-none-linux-gnu/include", - "/aarch64_be-none-linux-gnu/include/c++/14.2.1", - "/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu", - "/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward", - "/aarch64_be-none-linux-gnu/libc/usr/include", - ]); - } - - if !compiler.contains("clang") { - 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) - .to_string() - }) - .collect::>(); - - compile_c(&compiler_commands) -} - -pub fn build_c( - intrinsics: &Vec>, - compiler: Option<&str>, - target: &str, - cxx_toolchain_dir: Option<&str>, -) -> bool { - let intrinsics_name_list = intrinsics - .par_iter() - .map(|i| i.name.clone()) - .collect::>(); - let filename_mapping = create_c_filenames(&intrinsics_name_list); - - intrinsics.par_iter().for_each(|i| { - let c_code = generate_c_program_arm(&["arm_neon.h", "arm_acle.h", "arm_fp16.h"], i, target); - match filename_mapping.get(&i.name) { - Some(filename) => write_file(filename, c_code), - None => {} - }; - }); - - match compiler { - None => true, - Some(compiler) => compile_c_arm(&intrinsics_name_list, compiler, target, cxx_toolchain_dir), - } -} - -pub fn build_rust( - intrinsics: &[Intrinsic], - toolchain: Option<&str>, - target: &str, - linker: Option<&str>, -) -> bool { - let intrinsics_name_list = intrinsics - .par_iter() - .map(|i| i.name.clone()) - .collect::>(); - let filename_mapping = create_rust_filenames(&intrinsics_name_list); - - intrinsics.par_iter().for_each(|i| { - let rust_code = generate_rust_program_arm(i, target); - match filename_mapping.get(&i.name) { - Some(filename) => write_file(filename, rust_code), - None => {} - } - }); - - let intrinsics_name_list = intrinsics.iter().map(|i| i.name.as_str()).collect_vec(); - - compile_rust(&intrinsics_name_list, toolchain, target, linker) -} diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index a562f7b4258e..9e083a620932 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,16 +1,19 @@ +mod compile; mod config; -mod functions; mod intrinsic; mod json_parser; mod types; +use crate::arm::compile::compile_c_arm; use crate::arm::intrinsic::ArmIntrinsicType; use crate::common::SupportedArchitectureTest; use crate::common::cli::ProcessedCli; use crate::common::compare::compare_outputs; -use crate::common::intrinsic::Intrinsic; +use crate::common::gen_rust::compile_rust; +use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; use crate::common::intrinsic_helpers::{BaseIntrinsicTypeDefinition, TypeKind}; -use functions::{build_c, build_rust}; +use crate::common::write_file::{write_c_testfiles, write_rust_testfiles}; +use config::{AARCH_CONFIGURATIONS, POLY128_OSTREAM_DEF, build_notices}; use json_parser::get_neon_intrinsics; pub struct ArmArchitectureTest { @@ -48,21 +51,50 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } fn build_c_file(&self) -> bool { - build_c( - &self.intrinsics, - self.cli_options.cpp_compiler.as_deref(), - &self.cli_options.target, - self.cli_options.cxx_toolchain_dir.as_deref(), - ) + let compiler = self.cli_options.cpp_compiler.as_deref(); + let target = &self.cli_options.target; + let cxx_toolchain_dir = self.cli_options.cxx_toolchain_dir.as_deref(); + + let intrinsics_name_list = write_c_testfiles( + &self + .intrinsics + .iter() + .map(|i| i as &dyn IntrinsicDefinition<_>) + .collect::>(), + target, + &["arm_neon.h", "arm_acle.h", "arm_fp16.h"], + &build_notices("// "), + &[POLY128_OSTREAM_DEF], + ); + + match compiler { + None => true, + Some(compiler) => { + compile_c_arm(&intrinsics_name_list, compiler, target, cxx_toolchain_dir) + } + } } fn build_rust_file(&self) -> bool { - build_rust( - &self.intrinsics, - self.cli_options.toolchain.as_deref(), - &self.cli_options.target, - self.cli_options.linker.as_deref(), - ) + let final_target = if self.cli_options.target.contains("v7") { + "arm" + } else { + "aarch64" + }; + 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::>(), + final_target, + &build_notices("// "), + AARCH_CONFIGURATIONS, + ); + + compile_rust(intrinsics_name_list, toolchain, target, linker) } fn compare_outputs(&self) -> bool { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 305991468423..5c6a1efaec09 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -29,7 +29,7 @@ fn main() {{ } pub fn compile_rust( - binaries: &[&str], + binaries: Vec, toolchain: Option<&str>, target: &str, linker: Option<&str>, diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index d8d4fdd872ef..ace8dbf3a5db 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,6 +1,14 @@ use crate::common::argument::ArgumentList; use crate::common::indentation::Indentation; use crate::common::intrinsic_helpers::IntrinsicTypeDefinition; +use itertools::Itertools; + +use super::argument::Argument; +use super::gen_c::generate_c_program; +use super::gen_rust::generate_rust_program; + +// The number of times each intrinsic will be called. +const PASSES: u32 = 20; /// An intrinsic #[derive(Debug, PartialEq, Clone)] @@ -85,4 +93,132 @@ where args = self.arguments().as_call_param_rust(), ) } + + fn gen_code_c( + &self, + indentation: Indentation, + constraints: &[&Argument], + 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 = self.gen_code_c( + body_indentation, + constraints, + format!("{name}-{i}"), + target, + ) + ) + }) + .join("\n") + } else { + self.generate_loop_c(indentation, &name, PASSES, target) + } + } + + fn generate_c_program( + &self, + header_files: &[&str], + target: &str, + notices: &str, + arch_specific_definitions: &[&str], + ) -> String { + let arguments = self.arguments(); + let constraints = arguments + .iter() + .filter(|&i| i.has_constraint()) + .collect_vec(); + + let indentation = Indentation::default(); + generate_c_program( + notices, + header_files, + "aarch64", + arch_specific_definitions, + self.arguments() + .gen_arglists_c(indentation, PASSES) + .as_str(), + self.gen_code_c( + indentation.nested(), + constraints.as_slice(), + Default::default(), + target, + ) + .as_str(), + ) + } + + fn gen_code_rust( + &self, + indentation: Indentation, + constraints: &[&Argument], + 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 = self.gen_code_rust( + body_indentation, + constraints, + format!("{name}-{i}") + ) + ) + }) + .join("\n") + } else { + self.generate_loop_rust(indentation, &name, PASSES) + } + } + + fn generate_rust_program(&self, target: &str, notice: &str, cfg: &str) -> String { + let arguments = self.arguments(); + let constraints = arguments + .iter() + .filter(|i| i.has_constraint()) + .collect_vec(); + + let indentation = Indentation::default(); + generate_rust_program( + notice, + cfg, + target, + self.arguments() + .gen_arglists_rust(indentation.nested(), PASSES) + .as_str(), + self.gen_code_rust(indentation.nested(), &constraints, Default::default()) + .as_str(), + ) + } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 5f82755eb5c9..1045152f9e0e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -13,6 +13,7 @@ pub mod indentation; pub mod intrinsic; pub mod intrinsic_helpers; pub mod values; +pub mod write_file; /// Architectures must support this trait /// to be successfully tested. diff --git a/library/stdarch/crates/intrinsic-test/src/common/write_file.rs b/library/stdarch/crates/intrinsic-test/src/common/write_file.rs new file mode 100644 index 000000000000..adbf33397013 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/common/write_file.rs @@ -0,0 +1,52 @@ +use super::intrinsic_helpers::IntrinsicTypeDefinition; +use crate::common::gen_c::create_c_filenames; +use crate::common::gen_rust::create_rust_filenames; +use crate::common::intrinsic::IntrinsicDefinition; +use crate::common::write_file; + +pub fn write_c_testfiles( + intrinsics: &Vec<&dyn IntrinsicDefinition>, + 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 = create_c_filenames(&intrinsics_name_list); + + intrinsics.iter().for_each(|i| { + let c_code = i.generate_c_program(headers, target, notice, arch_specific_definitions); + match filename_mapping.get(&i.name()) { + Some(filename) => write_file(filename, c_code), + None => {} + }; + }); + + intrinsics_name_list +} + +pub fn write_rust_testfiles( + intrinsics: Vec<&dyn IntrinsicDefinition>, + rust_target: &str, + notice: &str, + cfg: &str, +) -> Vec { + let intrinsics_name_list = intrinsics + .iter() + .map(|i| i.name().clone()) + .collect::>(); + let filename_mapping = create_rust_filenames(&intrinsics_name_list); + + intrinsics.iter().for_each(|i| { + let rust_code = i.generate_rust_program(rust_target, notice, cfg); + match filename_mapping.get(&i.name()) { + Some(filename) => write_file(filename, rust_code), + None => {} + } + }); + + intrinsics_name_list +}