Skip to content

Commit 08325f3

Browse files
moved the C compilation commands into a struct for easier handling
1 parent d395e9c commit 08325f3

File tree

3 files changed

+193
-55
lines changed

3 files changed

+193
-55
lines changed

crates/intrinsic-test/src/arm/functions.rs

+41-55
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::config::{AARCH_CONFIGURATIONS, POLY128_OSTREAM_DEF, build_notices};
22
use super::intrinsic::ArmIntrinsicType;
33
use crate::arm::constraint::Constraint;
44
use crate::common::argument::Argument;
5+
use crate::common::compile_c::CompilationCommandBuilder;
56
use crate::common::format::Indentation;
67
use crate::common::gen_c::{compile_c, create_c_filenames, generate_c_program};
78
use crate::common::gen_rust::{compile_rust, create_rust_filenames, generate_rust_program};
@@ -161,70 +162,55 @@ fn generate_rust_program_arm(
161162

162163
fn compile_c_arm(
163164
intrinsics_name_list: &Vec<String>,
164-
filename_mapping: BTreeMap<&String, String>,
165+
_filename_mapping: BTreeMap<&String, String>,
165166
compiler: &str,
166167
target: &str,
167168
cxx_toolchain_dir: Option<&str>,
168169
) -> bool {
169-
let compiler_commands = intrinsics_name_list.iter().map(|intrinsic_name| {
170-
let c_filename = filename_mapping.get(intrinsic_name).unwrap();
171-
let flags = std::env::var("CPPFLAGS").unwrap_or("".into());
172-
let arch_flags = if target.contains("v7") {
173-
"-march=armv8.6-a+crypto+crc+dotprod+fp16"
174-
} else {
175-
"-march=armv8.6-a+crypto+sha3+crc+dotprod+fp16+faminmax+lut"
176-
};
170+
let mut command = CompilationCommandBuilder::new()
171+
.add_arch_flags(vec!["armv8.6-a", "crypto", "crc", "dotprod", "fp16"])
172+
.set_compiler(compiler)
173+
.set_target(target)
174+
.set_opt_level("2")
175+
.set_cxx_toolchain_dir(cxx_toolchain_dir)
176+
.set_project_root("c_programs")
177+
.add_extra_flags(vec!["-ffp-contract=off", "-Wno-narrowing"]);
177178

178-
let compiler_command = if target == "aarch64_be-unknown-linux-gnu" {
179-
let Some(cxx_toolchain_dir) = cxx_toolchain_dir else {
180-
panic!(
181-
"When setting `--target aarch64_be-unknown-linux-gnu` the C++ compilers toolchain directory must be set with `--cxx-toolchain-dir <dest>`"
182-
);
183-
};
179+
if !target.contains("v7") {
180+
command = command.add_arch_flags(vec!["faminmax", "lut", "sha3"]);
181+
}
184182

185-
/* clang++ cannot link an aarch64_be object file, so we invoke
186-
* aarch64_be-unknown-linux-gnu's C++ linker. This ensures that we
187-
* are testing the intrinsics against LLVM.
188-
*
189-
* Note: setting `--sysroot=<...>` which is the obvious thing to do
190-
* does not work as it gets caught up with `#include_next <stdlib.h>`
191-
* not existing... */
192-
format!(
193-
"{compiler} {flags} {arch_flags} \
194-
-ffp-contract=off \
195-
-Wno-narrowing \
196-
-O2 \
197-
--target=aarch64_be-unknown-linux-gnu \
198-
-I{cxx_toolchain_dir}/include \
199-
-I{cxx_toolchain_dir}/aarch64_be-none-linux-gnu/include \
200-
-I{cxx_toolchain_dir}/aarch64_be-none-linux-gnu/include/c++/14.2.1 \
201-
-I{cxx_toolchain_dir}/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu \
202-
-I{cxx_toolchain_dir}/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward \
203-
-I{cxx_toolchain_dir}/aarch64_be-none-linux-gnu/libc/usr/include \
204-
-c {c_filename} \
205-
-o c_programs/{intrinsic_name}.o && \
206-
{cxx_toolchain_dir}/bin/aarch64_be-none-linux-gnu-g++ c_programs/{intrinsic_name}.o -o c_programs/{intrinsic_name} && \
207-
rm c_programs/{intrinsic_name}.o",
183+
command = if target == "aarch64_be-unknown-linux-gnu" {
184+
command
185+
.set_linker(
186+
cxx_toolchain_dir.unwrap_or("").to_string() + "/bin/aarch64_be-none-linux-gnu-g++",
208187
)
188+
.set_include_paths(vec![
189+
"/include",
190+
"/aarch64_be-none-linux-gnu/include",
191+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1",
192+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu",
193+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward",
194+
"/aarch64_be-none-linux-gnu/libc/usr/include",
195+
])
196+
} else {
197+
if compiler.contains("clang") {
198+
command.add_extra_flag(format!("-target {target}").as_str())
209199
} else {
210-
// -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations
211-
let base_compiler_command = format!(
212-
"{compiler} {flags} {arch_flags} -o c_programs/{intrinsic_name} {c_filename} -ffp-contract=off -Wno-narrowing -O2"
213-
);
214-
215-
/* `-target` can be passed to some c++ compilers, however if we want to
216-
* use a c++ compiler does not support this flag we do not want to pass
217-
* the flag. */
218-
if compiler.contains("clang") {
219-
format!("{base_compiler_command} -target {target}")
220-
} else {
221-
format!("{base_compiler_command} -flax-vector-conversions")
222-
}
223-
};
200+
command.add_extra_flag("-flax-vector-conversions")
201+
}
202+
};
224203

225-
compiler_command
226-
})
227-
.collect::<Vec<_>>();
204+
let compiler_commands = intrinsics_name_list
205+
.iter()
206+
.map(|intrinsic_name| {
207+
command
208+
.clone()
209+
.set_input_name(intrinsic_name)
210+
.set_output_name(intrinsic_name)
211+
.to_string()
212+
})
213+
.collect::<Vec<_>>();
228214

229215
compile_c(&compiler_commands)
230216
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#[derive(Clone)]
2+
pub struct CompilationCommandBuilder {
3+
compiler: String,
4+
target: Option<String>,
5+
cxx_toolchain_dir: Option<String>,
6+
arch_flags: Vec<String>,
7+
optimization: String,
8+
include_paths: Vec<String>,
9+
project_root: Option<String>,
10+
output: String,
11+
input: String,
12+
linker: Option<String>,
13+
extra_flags: Vec<String>,
14+
}
15+
16+
impl CompilationCommandBuilder {
17+
pub fn new() -> Self {
18+
Self {
19+
compiler: String::new(),
20+
target: None,
21+
cxx_toolchain_dir: None,
22+
arch_flags: Vec::new(),
23+
optimization: "2".to_string(),
24+
include_paths: Vec::new(),
25+
project_root: None,
26+
output: String::new(),
27+
input: String::new(),
28+
linker: None,
29+
extra_flags: Vec::new(),
30+
}
31+
}
32+
33+
pub fn set_compiler(mut self, compiler: &str) -> Self {
34+
self.compiler = compiler.to_string();
35+
self
36+
}
37+
38+
pub fn set_target(mut self, target: &str) -> Self {
39+
self.target = Some(target.to_string());
40+
self
41+
}
42+
43+
pub fn set_cxx_toolchain_dir(mut self, path: Option<&str>) -> Self {
44+
self.cxx_toolchain_dir = path.map(|p| p.to_string());
45+
self
46+
}
47+
48+
pub fn add_arch_flags(mut self, flags: Vec<&str>) -> Self {
49+
let mut new_arch_flags = flags.into_iter().map(|v| v.to_string()).collect();
50+
self.arch_flags.append(&mut new_arch_flags);
51+
52+
self
53+
}
54+
55+
pub fn set_opt_level(mut self, optimization: &str) -> Self {
56+
self.optimization = optimization.to_string();
57+
self
58+
}
59+
60+
/// Sets a list of include paths for compilation.
61+
/// The paths that are passed must be relative to the
62+
/// "cxx_toolchain_dir" directory path.
63+
pub fn set_include_paths(mut self, paths: Vec<&str>) -> Self {
64+
self.include_paths = paths.into_iter().map(|path| path.to_string()).collect();
65+
self
66+
}
67+
68+
/// Sets the root path of all the generated test files.
69+
pub fn set_project_root(mut self, path: &str) -> Self {
70+
self.project_root = Some(path.to_string());
71+
self
72+
}
73+
74+
/// The name of the output executable, without any suffixes
75+
pub fn set_output_name(mut self, path: &str) -> Self {
76+
self.output = path.to_string();
77+
self
78+
}
79+
80+
/// The name of the input C file, without any suffixes
81+
pub fn set_input_name(mut self, path: &str) -> Self {
82+
self.input = path.to_string();
83+
self
84+
}
85+
86+
pub fn set_linker(mut self, linker: String) -> Self {
87+
self.linker = Some(linker);
88+
self.output += ".o";
89+
self
90+
}
91+
92+
pub fn add_extra_flags(mut self, flags: Vec<&str>) -> Self {
93+
let mut flags: Vec<String> = flags.into_iter().map(|f| f.to_string()).collect();
94+
self.extra_flags.append(&mut flags);
95+
self
96+
}
97+
98+
pub fn add_extra_flag(self, flag: &str) -> Self {
99+
self.add_extra_flags(vec![flag])
100+
}
101+
}
102+
103+
impl CompilationCommandBuilder {
104+
pub fn to_string(self) -> String {
105+
let arch_flags = self.arch_flags.join("+");
106+
let flags = std::env::var("CPPFLAGS").unwrap_or("".into());
107+
let project_root = self.project_root.unwrap_or(String::new());
108+
let project_root_str = project_root.as_str();
109+
let mut command = format!(
110+
"{} {flags} -march={arch_flags} \
111+
-O{} \
112+
-o {project_root}/{} \
113+
{project_root}/{}.cpp",
114+
self.compiler, self.optimization, self.output, self.input,
115+
);
116+
117+
command = command + " " + self.extra_flags.join(" ").as_str();
118+
119+
if let (Some(linker), Some(cxx_toolchain_dir)) = (&self.linker, &self.cxx_toolchain_dir) {
120+
if let Some(target) = &self.target {
121+
command = command + " --target=" + target;
122+
}
123+
124+
let include_args = self
125+
.include_paths
126+
.iter()
127+
.map(|path| "--include-directory=".to_string() + cxx_toolchain_dir + path)
128+
.collect::<Vec<_>>()
129+
.join(" ");
130+
131+
command = command
132+
+ " -c "
133+
+ include_args.as_str()
134+
+ " && "
135+
+ linker
136+
+ project_root_str
137+
+ "/"
138+
+ self.output.as_str()
139+
+ " -o "
140+
+ project_root_str
141+
+ "/"
142+
+ self.output.strip_suffix(".o").unwrap()
143+
+ " && rm "
144+
+ project_root_str
145+
+ "/"
146+
+ self.output.as_str();
147+
}
148+
149+
command
150+
}
151+
}

crates/intrinsic-test/src/common/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::io::Write;
44

55
pub mod argument;
66
pub mod compare;
7+
pub mod compile_c;
78
pub mod format;
89
pub mod gen_c;
910
pub mod gen_rust;

0 commit comments

Comments
 (0)