Skip to content

Commit ac478c8

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

File tree

3 files changed

+192
-56
lines changed

3 files changed

+192
-56
lines changed

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

Lines changed: 39 additions & 56 deletions
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,52 @@ 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-
};
177-
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-
};
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"]);
184178

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",
179+
command = if target == "aarch64_be-unknown-linux-gnu" {
180+
command
181+
.set_linker(
182+
cxx_toolchain_dir.unwrap_or("").to_string()
183+
+ "/bin/aarch64_be-none-linux-gnu-g++",
208184
)
185+
.set_include_paths(vec![
186+
"/include",
187+
"/aarch64_be-none-linux-gnu/include",
188+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1",
189+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1/aarch64_be-none-linux-gnu",
190+
"/aarch64_be-none-linux-gnu/include/c++/14.2.1/backward",
191+
"/aarch64_be-none-linux-gnu/libc/usr/include",
192+
])
193+
} else {
194+
if compiler.contains("clang") {
195+
command.add_extra_flag(format!("-target {target}").as_str())
209196
} 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-
};
197+
command.add_extra_flag("-flax-vector-conversions")
198+
}
199+
};
224200

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

229212
compile_c(&compiler_commands)
230213
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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
51+
.append(&mut new_arch_flags);
52+
53+
self
54+
}
55+
56+
pub fn set_opt_level(mut self, optimization: &str) -> Self {
57+
self.optimization = optimization.to_string();
58+
self
59+
}
60+
61+
/// Sets a list of include paths for compilation.
62+
/// The paths that are passed must be relative to the
63+
/// "cxx_toolchain_dir" directory path.
64+
pub fn set_include_paths(mut self, paths: Vec<&str>) -> Self {
65+
self.include_paths = paths.into_iter().map(|path| path.to_string()).collect();
66+
self
67+
}
68+
69+
/// Sets the root path of all the generated test files.
70+
pub fn set_project_root(mut self, path: &str) -> Self {
71+
self.project_root = Some(path.to_string());
72+
self
73+
}
74+
75+
/// The name of the output executable, without any suffixes
76+
pub fn set_output_name(mut self, path: &str) -> Self {
77+
self.output = path.to_string();
78+
self
79+
}
80+
81+
/// The name of the input C file, without any suffixes
82+
pub fn set_input_name(mut self, path: &str) -> Self {
83+
self.input = path.to_string();
84+
self
85+
}
86+
87+
pub fn set_linker(mut self, linker: String) -> Self {
88+
self.linker = Some(linker);
89+
self.output += ".o";
90+
self
91+
}
92+
93+
pub fn add_extra_flags(mut self, flags: Vec<&str>) -> Self {
94+
let mut flags: Vec<String> = flags.into_iter().map(|f| f.to_string()).collect();
95+
self.extra_flags.append(&mut flags);
96+
self
97+
}
98+
99+
pub fn add_extra_flag(self, flag: &str) -> Self {
100+
self.add_extra_flags(vec![flag])
101+
}
102+
}
103+
104+
impl CompilationCommandBuilder {
105+
pub fn to_string(self) -> String {
106+
let arch_flags = self.arch_flags.join("+");
107+
let flags = std::env::var("CPPFLAGS").unwrap_or("".into());
108+
let project_root = self.project_root.unwrap_or(String::new());
109+
let project_root_str = project_root.as_str();
110+
let mut command = format!(
111+
"{} {flags} -march={arch_flags} \
112+
-O{} \
113+
-o {project_root}/{} \
114+
{project_root}/{}.cpp",
115+
self.compiler, self.optimization, self.output, self.input,
116+
);
117+
118+
command = command + " " + self.extra_flags.join(" ").as_str();
119+
120+
if let (Some(linker), Some(cxx_toolchain_dir)) = (&self.linker, &self.cxx_toolchain_dir) {
121+
if let Some(target) = &self.target {
122+
command = command + " --target=" + target;
123+
}
124+
125+
let include_args = self
126+
.include_paths
127+
.iter()
128+
.map(|path| "--include-directory=".to_string() + cxx_toolchain_dir + path)
129+
.collect::<Vec<_>>()
130+
.join(" ");
131+
132+
command = command
133+
+ " -c "
134+
+ include_args.as_str()
135+
+ " && "
136+
+ linker
137+
+ project_root_str
138+
+ "/"
139+
+ self.output.as_str()
140+
+ " -o "
141+
+ project_root_str
142+
+ "/"
143+
+ self.output.strip_suffix(".o").unwrap()
144+
+ " && rm "
145+
+ project_root_str
146+
+ "/"
147+
+ self.output.as_str();
148+
}
149+
150+
command
151+
}
152+
}

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

Lines changed: 1 addition & 0 deletions
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)