2021-11-05 01:47:31 +00:00
|
|
|
#![feature(slice_partition_dedup)]
|
2021-09-09 19:16:45 +01:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate log;
|
|
|
|
|
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io::Write;
|
2023-10-03 20:07:20 +02:00
|
|
|
use std::path::PathBuf;
|
2021-09-09 19:16:45 +01:00
|
|
|
use std::process::Command;
|
|
|
|
|
|
|
|
|
|
use intrinsic::Intrinsic;
|
2021-11-05 01:47:31 +00:00
|
|
|
use itertools::Itertools;
|
2021-09-09 19:16:45 +01:00
|
|
|
use rayon::prelude::*;
|
|
|
|
|
use types::TypeKind;
|
|
|
|
|
|
2021-11-05 01:47:31 +00:00
|
|
|
use crate::argument::Argument;
|
2023-07-28 15:09:46 +01:00
|
|
|
use crate::format::Indentation;
|
2023-04-11 15:46:03 +01:00
|
|
|
use crate::json_parser::get_neon_intrinsics;
|
2021-11-05 01:47:31 +00:00
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
mod argument;
|
2023-07-28 15:09:46 +01:00
|
|
|
mod format;
|
2021-09-09 19:16:45 +01:00
|
|
|
mod intrinsic;
|
2023-04-11 15:46:03 +01:00
|
|
|
mod json_parser;
|
2021-09-09 19:16:45 +01:00
|
|
|
mod types;
|
|
|
|
|
mod values;
|
|
|
|
|
|
2022-06-21 18:42:39 +01:00
|
|
|
// The number of times each intrinsic will be called.
|
|
|
|
|
const PASSES: u32 = 20;
|
|
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
pub enum Language {
|
|
|
|
|
Rust,
|
|
|
|
|
C,
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-21 18:42:39 +01:00
|
|
|
fn gen_code_c(
|
2023-07-28 15:09:46 +01:00
|
|
|
indentation: Indentation,
|
2022-06-21 18:42:39 +01:00
|
|
|
intrinsic: &Intrinsic,
|
|
|
|
|
constraints: &[&Argument],
|
|
|
|
|
name: String,
|
|
|
|
|
p64_armv7_workaround: bool,
|
|
|
|
|
) -> String {
|
2021-11-05 01:47:31 +00:00
|
|
|
if let Some((current, constraints)) = constraints.split_last() {
|
|
|
|
|
let range = current
|
|
|
|
|
.constraints
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|c| c.to_range())
|
|
|
|
|
.flat_map(|r| r.into_iter());
|
|
|
|
|
|
2023-07-28 15:09:46 +01:00
|
|
|
let body_indentation = indentation.nested();
|
2021-11-05 01:47:31 +00:00
|
|
|
range
|
|
|
|
|
.map(|i| {
|
|
|
|
|
format!(
|
2023-07-28 15:09:46 +01:00
|
|
|
"{indentation}{{\n\
|
|
|
|
|
{body_indentation}{ty} {name} = {val};\n\
|
|
|
|
|
{pass}\n\
|
|
|
|
|
{indentation}}}",
|
2021-11-05 01:47:31 +00:00
|
|
|
name = current.name,
|
|
|
|
|
ty = current.ty.c_type(),
|
|
|
|
|
val = i,
|
2022-06-21 18:42:39 +01:00
|
|
|
pass = gen_code_c(
|
2023-07-28 15:09:46 +01:00
|
|
|
body_indentation,
|
2022-06-21 18:42:39 +01:00
|
|
|
intrinsic,
|
|
|
|
|
constraints,
|
2022-10-25 15:17:23 -04:00
|
|
|
format!("{name}-{i}"),
|
2022-06-21 18:42:39 +01:00
|
|
|
p64_armv7_workaround
|
|
|
|
|
)
|
2021-11-05 01:47:31 +00:00
|
|
|
)
|
|
|
|
|
})
|
2023-07-28 15:09:46 +01:00
|
|
|
.join("\n")
|
2021-11-05 01:47:31 +00:00
|
|
|
} else {
|
2023-07-28 15:09:46 +01:00
|
|
|
intrinsic.generate_loop_c(indentation, &name, PASSES, p64_armv7_workaround)
|
2021-11-05 01:47:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-21 18:42:39 +01:00
|
|
|
fn generate_c_program(
|
2022-10-31 12:27:04 +00:00
|
|
|
notices: &str,
|
2022-06-21 18:42:39 +01:00
|
|
|
header_files: &[&str],
|
|
|
|
|
intrinsic: &Intrinsic,
|
|
|
|
|
p64_armv7_workaround: bool,
|
|
|
|
|
) -> String {
|
2021-11-05 01:47:31 +00:00
|
|
|
let constraints = intrinsic
|
|
|
|
|
.arguments
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|i| i.has_constraint())
|
|
|
|
|
.collect_vec();
|
|
|
|
|
|
2023-07-28 15:09:46 +01:00
|
|
|
let indentation = Indentation::default();
|
2021-09-09 19:16:45 +01:00
|
|
|
format!(
|
2022-10-31 12:27:04 +00:00
|
|
|
r#"{notices}{header_files}
|
2021-09-09 19:16:45 +01:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <sstream>
|
2021-12-04 13:03:30 +00:00
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
template<typename T1, typename T2> T1 cast(T2 x) {{
|
|
|
|
|
static_assert(sizeof(T1) == sizeof(T2), "sizeof T1 and T2 must be the same");
|
2022-06-21 18:42:39 +01:00
|
|
|
T1 ret{{}};
|
2021-09-09 19:16:45 +01:00
|
|
|
memcpy(&ret, &x, sizeof(T1));
|
|
|
|
|
return ret;
|
|
|
|
|
}}
|
2021-12-04 13:03:30 +00:00
|
|
|
|
|
|
|
|
#ifdef __aarch64__
|
2021-09-09 19:16:45 +01:00
|
|
|
std::ostream& operator<<(std::ostream& os, poly128_t value) {{
|
|
|
|
|
std::stringstream temp;
|
|
|
|
|
do {{
|
|
|
|
|
int n = value % 10;
|
|
|
|
|
value /= 10;
|
|
|
|
|
temp << n;
|
|
|
|
|
}} while (value != 0);
|
|
|
|
|
std::string tempstr(temp.str());
|
|
|
|
|
std::string res(tempstr.rbegin(), tempstr.rend());
|
|
|
|
|
os << res;
|
|
|
|
|
return os;
|
|
|
|
|
}}
|
2021-12-04 13:03:30 +00:00
|
|
|
#endif
|
|
|
|
|
|
2022-06-21 18:42:39 +01:00
|
|
|
{arglists}
|
|
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
int main(int argc, char **argv) {{
|
|
|
|
|
{passes}
|
|
|
|
|
return 0;
|
|
|
|
|
}}"#,
|
|
|
|
|
header_files = header_files
|
|
|
|
|
.iter()
|
2022-10-25 15:17:23 -04:00
|
|
|
.map(|header| format!("#include <{header}>"))
|
2021-09-09 19:16:45 +01:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join("\n"),
|
2023-07-28 15:09:46 +01:00
|
|
|
arglists = intrinsic.arguments.gen_arglists_c(indentation, PASSES),
|
2022-06-21 18:42:39 +01:00
|
|
|
passes = gen_code_c(
|
2023-07-28 15:09:46 +01:00
|
|
|
indentation.nested(),
|
2022-06-21 18:42:39 +01:00
|
|
|
intrinsic,
|
|
|
|
|
constraints.as_slice(),
|
|
|
|
|
Default::default(),
|
|
|
|
|
p64_armv7_workaround
|
|
|
|
|
),
|
2021-09-09 19:16:45 +01:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 15:09:46 +01:00
|
|
|
fn gen_code_rust(
|
|
|
|
|
indentation: Indentation,
|
|
|
|
|
intrinsic: &Intrinsic,
|
|
|
|
|
constraints: &[&Argument],
|
|
|
|
|
name: String,
|
|
|
|
|
) -> String {
|
2021-11-05 01:47:31 +00:00
|
|
|
if let Some((current, constraints)) = constraints.split_last() {
|
|
|
|
|
let range = current
|
|
|
|
|
.constraints
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|c| c.to_range())
|
|
|
|
|
.flat_map(|r| r.into_iter());
|
|
|
|
|
|
2023-07-28 15:09:46 +01:00
|
|
|
let body_indentation = indentation.nested();
|
2021-11-05 01:47:31 +00:00
|
|
|
range
|
|
|
|
|
.map(|i| {
|
|
|
|
|
format!(
|
2023-07-28 15:09:46 +01:00
|
|
|
"{indentation}{{\n\
|
|
|
|
|
{body_indentation}const {name}: {ty} = {val};\n\
|
|
|
|
|
{pass}\n\
|
|
|
|
|
{indentation}}}",
|
2021-11-05 01:47:31 +00:00
|
|
|
name = current.name,
|
|
|
|
|
ty = current.ty.rust_type(),
|
|
|
|
|
val = i,
|
2023-07-28 15:09:46 +01:00
|
|
|
pass = gen_code_rust(
|
|
|
|
|
body_indentation,
|
|
|
|
|
intrinsic,
|
|
|
|
|
constraints,
|
|
|
|
|
format!("{name}-{i}")
|
|
|
|
|
)
|
2021-11-05 01:47:31 +00:00
|
|
|
)
|
|
|
|
|
})
|
2023-07-28 15:09:46 +01:00
|
|
|
.join("\n")
|
2021-11-05 01:47:31 +00:00
|
|
|
} else {
|
2023-07-28 15:09:46 +01:00
|
|
|
intrinsic.generate_loop_rust(indentation, &name, PASSES)
|
2021-11-05 01:47:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 12:27:04 +00:00
|
|
|
fn generate_rust_program(notices: &str, intrinsic: &Intrinsic, a32: bool) -> String {
|
2021-11-05 01:47:31 +00:00
|
|
|
let constraints = intrinsic
|
|
|
|
|
.arguments
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|i| i.has_constraint())
|
|
|
|
|
.collect_vec();
|
|
|
|
|
|
2023-07-28 15:09:46 +01:00
|
|
|
let indentation = Indentation::default();
|
2021-09-09 19:16:45 +01:00
|
|
|
format!(
|
2022-10-31 12:27:04 +00:00
|
|
|
r#"{notices}#![feature(simd_ffi)]
|
2021-09-09 19:16:45 +01:00
|
|
|
#![feature(link_llvm_intrinsics)]
|
2023-10-27 13:37:04 +01:00
|
|
|
#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))]
|
|
|
|
|
#![feature(stdarch_arm_crc32)]
|
2023-11-30 15:00:25 -08:00
|
|
|
#![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))]
|
2021-11-05 01:47:31 +00:00
|
|
|
#![allow(non_upper_case_globals)]
|
2021-12-04 13:03:30 +00:00
|
|
|
use core_arch::arch::{target_arch}::*;
|
2021-09-09 19:16:45 +01:00
|
|
|
|
|
|
|
|
fn main() {{
|
2023-05-19 16:27:05 +01:00
|
|
|
{arglists}
|
2021-09-09 19:16:45 +01:00
|
|
|
{passes}
|
|
|
|
|
}}
|
|
|
|
|
"#,
|
2021-12-04 13:03:30 +00:00
|
|
|
target_arch = if a32 { "arm" } else { "aarch64" },
|
2023-07-28 15:09:46 +01:00
|
|
|
arglists = intrinsic
|
|
|
|
|
.arguments
|
|
|
|
|
.gen_arglists_rust(indentation.nested(), PASSES),
|
|
|
|
|
passes = gen_code_rust(
|
|
|
|
|
indentation.nested(),
|
|
|
|
|
intrinsic,
|
|
|
|
|
&constraints,
|
|
|
|
|
Default::default()
|
|
|
|
|
)
|
2021-09-09 19:16:45 +01:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-04 13:03:30 +00:00
|
|
|
fn compile_c(c_filename: &str, intrinsic: &Intrinsic, compiler: &str, a32: bool) -> bool {
|
2021-09-09 19:16:45 +01:00
|
|
|
let flags = std::env::var("CPPFLAGS").unwrap_or("".into());
|
|
|
|
|
|
|
|
|
|
let output = Command::new("sh")
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(format!(
|
2023-04-11 15:46:03 +01:00
|
|
|
// -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations
|
|
|
|
|
"{cpp} {cppflags} {arch_flags} -ffp-contract=off -Wno-narrowing -O2 -target {target} -o c_programs/{intrinsic} {filename}",
|
2021-12-04 13:03:30 +00:00
|
|
|
target = if a32 { "armv7-unknown-linux-gnueabihf" } else { "aarch64-unknown-linux-gnu" },
|
|
|
|
|
arch_flags = if a32 { "-march=armv8.6-a+crypto+crc+dotprod" } else { "-march=armv8.6-a+crypto+sha3+crc+dotprod" },
|
2021-09-09 19:16:45 +01:00
|
|
|
filename = c_filename,
|
|
|
|
|
intrinsic = intrinsic.name,
|
|
|
|
|
cpp = compiler,
|
|
|
|
|
cppflags = flags,
|
|
|
|
|
))
|
|
|
|
|
.output();
|
|
|
|
|
if let Ok(output) = output {
|
|
|
|
|
if output.status.success() {
|
|
|
|
|
true
|
|
|
|
|
} else {
|
2021-12-04 13:03:30 +00:00
|
|
|
error!(
|
|
|
|
|
"Failed to compile code for intrinsic: {}\n\nstdout:\n{}\n\nstderr:\n{}",
|
|
|
|
|
intrinsic.name,
|
|
|
|
|
std::str::from_utf8(&output.stdout).unwrap_or(""),
|
|
|
|
|
std::str::from_utf8(&output.stderr).unwrap_or("")
|
|
|
|
|
);
|
|
|
|
|
false
|
2021-09-09 19:16:45 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
error!("Command failed: {:#?}", output);
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-11 15:46:03 +01:00
|
|
|
fn build_notices(line_prefix: &str) -> String {
|
|
|
|
|
format!(
|
2022-10-31 12:27:04 +00:00
|
|
|
"\
|
|
|
|
|
{line_prefix}This is a transient test file, not intended for distribution. Some aspects of the
|
2023-04-11 15:46:03 +01:00
|
|
|
{line_prefix}test are derived from a JSON specification, published under the same license as the
|
|
|
|
|
{line_prefix}`intrinsic-test` crate.\n
|
2022-10-31 12:27:04 +00:00
|
|
|
"
|
2023-04-11 15:46:03 +01:00
|
|
|
)
|
2022-10-31 12:27:04 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
fn build_c(notices: &str, intrinsics: &Vec<Intrinsic>, compiler: Option<&str>, a32: bool) -> bool {
|
2021-09-09 19:16:45 +01:00
|
|
|
let _ = std::fs::create_dir("c_programs");
|
|
|
|
|
intrinsics
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|i| {
|
|
|
|
|
let c_filename = format!(r#"c_programs/{}.cpp"#, i.name);
|
|
|
|
|
let mut file = File::create(&c_filename).unwrap();
|
|
|
|
|
|
2023-10-30 13:49:59 +01:00
|
|
|
let c_code = generate_c_program(notices, &["arm_neon.h", "arm_acle.h"], i, a32);
|
2021-09-09 19:16:45 +01:00
|
|
|
file.write_all(c_code.into_bytes().as_slice()).unwrap();
|
2023-07-28 11:22:12 +01:00
|
|
|
match compiler {
|
|
|
|
|
None => true,
|
2023-10-30 13:49:59 +01:00
|
|
|
Some(compiler) => compile_c(&c_filename, i, compiler, a32),
|
2023-07-28 11:22:12 +01:00
|
|
|
}
|
2021-09-09 19:16:45 +01:00
|
|
|
})
|
|
|
|
|
.find_any(|x| !x)
|
|
|
|
|
.is_none()
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
fn build_rust(notices: &str, intrinsics: &[Intrinsic], toolchain: Option<&str>, a32: bool) -> bool {
|
2021-09-09 19:16:45 +01:00
|
|
|
intrinsics.iter().for_each(|i| {
|
|
|
|
|
let rust_dir = format!(r#"rust_programs/{}"#, i.name);
|
|
|
|
|
let _ = std::fs::create_dir_all(&rust_dir);
|
2022-10-25 15:17:23 -04:00
|
|
|
let rust_filename = format!(r#"{rust_dir}/main.rs"#);
|
2021-09-09 19:16:45 +01:00
|
|
|
let mut file = File::create(&rust_filename).unwrap();
|
|
|
|
|
|
2023-10-30 13:49:59 +01:00
|
|
|
let c_code = generate_rust_program(notices, i, a32);
|
2021-09-09 19:16:45 +01:00
|
|
|
file.write_all(c_code.into_bytes().as_slice()).unwrap();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let mut cargo = File::create("rust_programs/Cargo.toml").unwrap();
|
|
|
|
|
cargo
|
|
|
|
|
.write_all(
|
|
|
|
|
format!(
|
|
|
|
|
r#"[package]
|
2022-10-31 12:27:04 +00:00
|
|
|
name = "intrinsic-test-programs"
|
2021-09-09 19:16:45 +01:00
|
|
|
version = "{version}"
|
2023-05-19 16:49:41 +01:00
|
|
|
authors = [{authors}]
|
2023-04-11 15:46:03 +01:00
|
|
|
license = "{license}"
|
2021-09-09 19:16:45 +01:00
|
|
|
edition = "2018"
|
|
|
|
|
[workspace]
|
|
|
|
|
[dependencies]
|
|
|
|
|
core_arch = {{ path = "../crates/core_arch" }}
|
|
|
|
|
{binaries}"#,
|
|
|
|
|
version = env!("CARGO_PKG_VERSION"),
|
2023-05-19 16:49:41 +01:00
|
|
|
authors = env!("CARGO_PKG_AUTHORS")
|
|
|
|
|
.split(":")
|
|
|
|
|
.format_with(", ", |author, fmt| fmt(&format_args!("\"{author}\""))),
|
2023-04-11 15:46:03 +01:00
|
|
|
license = env!("CARGO_PKG_LICENSE"),
|
2021-09-09 19:16:45 +01:00
|
|
|
binaries = intrinsics
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|i| {
|
|
|
|
|
format!(
|
|
|
|
|
r#"[[bin]]
|
|
|
|
|
name = "{intrinsic}"
|
|
|
|
|
path = "{intrinsic}/main.rs""#,
|
|
|
|
|
intrinsic = i.name
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join("\n")
|
|
|
|
|
)
|
|
|
|
|
.into_bytes()
|
|
|
|
|
.as_slice(),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
let toolchain = match toolchain {
|
|
|
|
|
None => return true,
|
|
|
|
|
Some(t) => t,
|
|
|
|
|
};
|
|
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
let output = Command::new("sh")
|
|
|
|
|
.current_dir("rust_programs")
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(format!(
|
2022-06-21 18:42:39 +01:00
|
|
|
"cargo {toolchain} build --target {target} --release",
|
2021-09-09 19:16:45 +01:00
|
|
|
toolchain = toolchain,
|
2021-12-04 13:03:30 +00:00
|
|
|
target = if a32 {
|
|
|
|
|
"armv7-unknown-linux-gnueabihf"
|
|
|
|
|
} else {
|
|
|
|
|
"aarch64-unknown-linux-gnu"
|
|
|
|
|
},
|
2021-09-09 19:16:45 +01:00
|
|
|
))
|
2021-12-04 13:03:30 +00:00
|
|
|
.env("RUSTFLAGS", "-Cdebuginfo=0")
|
2021-09-09 19:16:45 +01:00
|
|
|
.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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 20:07:20 +02:00
|
|
|
/// Intrinsic test tool
|
|
|
|
|
#[derive(clap::Parser)]
|
|
|
|
|
#[command(
|
|
|
|
|
name = "Intrinsic test tool",
|
|
|
|
|
about = "Generates Rust and C programs for intrinsics and compares the output"
|
|
|
|
|
)]
|
|
|
|
|
struct Cli {
|
|
|
|
|
/// The input file containing the intrinsics
|
|
|
|
|
input: PathBuf,
|
|
|
|
|
|
|
|
|
|
/// The rust toolchain to use for building the rust code
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
toolchain: Option<String>,
|
|
|
|
|
|
|
|
|
|
/// The C++ compiler to use for compiling the c++ code
|
|
|
|
|
#[arg(long, default_value_t = String::from("clang++"))]
|
|
|
|
|
cppcompiler: String,
|
|
|
|
|
|
|
|
|
|
/// Run the C programs under emulation with this command
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
runner: Option<String>,
|
|
|
|
|
|
|
|
|
|
/// Filename for a list of intrinsics to skip (one per line)
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
skip: Option<PathBuf>,
|
|
|
|
|
|
|
|
|
|
/// Run tests for A32 instrinsics instead of A64
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
a32: bool,
|
2023-07-28 11:22:12 +01:00
|
|
|
|
|
|
|
|
/// Regenerate test programs, but don't build or run them
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
generate_only: bool,
|
2023-10-03 20:07:20 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-09 19:16:45 +01:00
|
|
|
fn main() {
|
|
|
|
|
pretty_env_logger::init();
|
|
|
|
|
|
2023-10-03 20:07:20 +02:00
|
|
|
let args: Cli = clap::Parser::parse();
|
2021-09-09 19:16:45 +01:00
|
|
|
|
2023-10-03 20:07:20 +02:00
|
|
|
let filename = args.input;
|
|
|
|
|
let c_runner = args.runner.unwrap_or_else(String::new);
|
|
|
|
|
let skip = if let Some(filename) = args.skip {
|
2021-11-05 01:47:31 +00:00
|
|
|
let data = std::fs::read_to_string(&filename).expect("Failed to open file");
|
2021-12-04 13:03:30 +00:00
|
|
|
data.lines()
|
|
|
|
|
.map(str::trim)
|
|
|
|
|
.filter(|s| !s.contains('#'))
|
|
|
|
|
.map(String::from)
|
|
|
|
|
.collect_vec()
|
2021-11-05 01:47:31 +00:00
|
|
|
} else {
|
|
|
|
|
Default::default()
|
|
|
|
|
};
|
2023-10-03 20:07:20 +02:00
|
|
|
let a32 = args.a32;
|
|
|
|
|
let mut intrinsics = get_neon_intrinsics(&filename).expect("Error parsing input file");
|
2021-11-05 01:47:31 +00:00
|
|
|
|
2023-04-11 15:46:03 +01:00
|
|
|
intrinsics.sort_by(|a, b| a.name.cmp(&b.name));
|
2021-11-05 01:47:31 +00:00
|
|
|
|
|
|
|
|
let mut intrinsics = intrinsics
|
|
|
|
|
.into_iter()
|
2021-09-09 19:16:45 +01:00
|
|
|
// Not sure how we would compare intrinsic that returns void.
|
|
|
|
|
.filter(|i| i.results.kind() != TypeKind::Void)
|
|
|
|
|
.filter(|i| i.results.kind() != TypeKind::BFloat)
|
|
|
|
|
.filter(|i| !(i.results.kind() == TypeKind::Float && i.results.inner_size() == 16))
|
2021-11-05 01:47:31 +00:00
|
|
|
.filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat))
|
2021-09-09 19:16:45 +01:00
|
|
|
.filter(|i| {
|
2021-11-05 01:47:31 +00:00
|
|
|
!i.arguments
|
2021-09-09 19:16:45 +01:00
|
|
|
.iter()
|
2021-11-05 01:47:31 +00:00
|
|
|
.any(|a| a.ty.kind() == TypeKind::Float && a.ty.inner_size() == 16)
|
2021-09-09 19:16:45 +01:00
|
|
|
})
|
|
|
|
|
// Skip pointers for now, we would probably need to look at the return
|
|
|
|
|
// type to work out how many elements we need to point to.
|
2021-11-05 01:47:31 +00:00
|
|
|
.filter(|i| !i.arguments.iter().any(|a| a.is_ptr()))
|
|
|
|
|
.filter(|i| !i.arguments.iter().any(|a| a.ty.inner_size() == 128))
|
|
|
|
|
.filter(|i| !skip.contains(&i.name))
|
2021-12-04 13:03:30 +00:00
|
|
|
.filter(|i| !(a32 && i.a64_only))
|
2021-09-09 19:16:45 +01:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
intrinsics.dedup();
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
let (toolchain, cpp_compiler) = if args.generate_only {
|
|
|
|
|
(None, None)
|
|
|
|
|
} else {
|
|
|
|
|
(
|
|
|
|
|
Some(args.toolchain.map_or_else(String::new, |t| format!("+{t}"))),
|
|
|
|
|
Some(args.cppcompiler),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-11 15:46:03 +01:00
|
|
|
let notices = build_notices("// ");
|
2022-10-31 12:27:04 +00:00
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
if !build_c(¬ices, &intrinsics, cpp_compiler.as_deref(), a32) {
|
2021-09-09 19:16:45 +01:00
|
|
|
std::process::exit(2);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
if !build_rust(¬ices, &intrinsics, toolchain.as_deref(), a32) {
|
2021-09-09 19:16:45 +01:00
|
|
|
std::process::exit(3);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-28 11:22:12 +01:00
|
|
|
if let Some(ref toolchain) = toolchain {
|
|
|
|
|
if !compare_outputs(&intrinsics, toolchain, &c_runner, a32) {
|
|
|
|
|
std::process::exit(1)
|
|
|
|
|
}
|
2021-09-09 19:16:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum FailureReason {
|
|
|
|
|
RunC(String),
|
|
|
|
|
RunRust(String),
|
|
|
|
|
Difference(String, String, String),
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-04 13:03:30 +00:00
|
|
|
fn compare_outputs(intrinsics: &Vec<Intrinsic>, toolchain: &str, runner: &str, a32: bool) -> bool {
|
2021-09-09 19:16:45 +01:00
|
|
|
let intrinsics = intrinsics
|
|
|
|
|
.par_iter()
|
|
|
|
|
.filter_map(|intrinsic| {
|
|
|
|
|
let c = Command::new("sh")
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(format!(
|
|
|
|
|
"{runner} ./c_programs/{intrinsic}",
|
|
|
|
|
runner = runner,
|
|
|
|
|
intrinsic = intrinsic.name,
|
|
|
|
|
))
|
|
|
|
|
.output();
|
|
|
|
|
let rust = Command::new("sh")
|
|
|
|
|
.current_dir("rust_programs")
|
|
|
|
|
.arg("-c")
|
|
|
|
|
.arg(format!(
|
2022-06-21 18:42:39 +01:00
|
|
|
"cargo {toolchain} run --target {target} --bin {intrinsic} --release",
|
2021-09-09 19:16:45 +01:00
|
|
|
intrinsic = intrinsic.name,
|
|
|
|
|
toolchain = toolchain,
|
2021-12-04 13:03:30 +00:00
|
|
|
target = if a32 {
|
|
|
|
|
"armv7-unknown-linux-gnueabihf"
|
|
|
|
|
} else {
|
|
|
|
|
"aarch64-unknown-linux-gnu"
|
|
|
|
|
},
|
2021-09-09 19:16:45 +01:00
|
|
|
))
|
2021-12-04 13:03:30 +00:00
|
|
|
.env("RUSTFLAGS", "-Cdebuginfo=0")
|
2021-09-09 19:16:45 +01:00
|
|
|
.output();
|
|
|
|
|
|
|
|
|
|
let (c, rust) = match (c, rust) {
|
|
|
|
|
(Ok(c), Ok(rust)) => (c, rust),
|
2022-10-25 15:17:23 -04:00
|
|
|
a => panic!("{a:#?}"),
|
2021-09-09 19:16:45 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if !c.status.success() {
|
|
|
|
|
error!("Failed to run C program for intrinsic {}", intrinsic.name);
|
|
|
|
|
return Some(FailureReason::RunC(intrinsic.name.clone()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !rust.status.success() {
|
|
|
|
|
error!(
|
|
|
|
|
"Failed to run rust program for intrinsic {}",
|
|
|
|
|
intrinsic.name
|
|
|
|
|
);
|
|
|
|
|
return Some(FailureReason::RunRust(intrinsic.name.clone()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!("Comparing intrinsic: {}", intrinsic.name);
|
|
|
|
|
|
|
|
|
|
let c = std::str::from_utf8(&c.stdout)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_lowercase()
|
|
|
|
|
.replace("-nan", "nan");
|
|
|
|
|
let rust = std::str::from_utf8(&rust.stdout)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_lowercase()
|
|
|
|
|
.replace("-nan", "nan");
|
|
|
|
|
|
|
|
|
|
if c == rust {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(FailureReason::Difference(intrinsic.name.clone(), c, rust))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
intrinsics.iter().for_each(|reason| match reason {
|
|
|
|
|
FailureReason::Difference(intrinsic, c, rust) => {
|
2022-10-25 15:17:23 -04:00
|
|
|
println!("Difference for intrinsic: {intrinsic}");
|
2021-09-09 19:16:45 +01:00
|
|
|
let diff = diff::lines(c, rust);
|
|
|
|
|
diff.iter().for_each(|diff| match diff {
|
2022-10-25 15:17:23 -04:00
|
|
|
diff::Result::Left(c) => println!("C: {c}"),
|
|
|
|
|
diff::Result::Right(rust) => println!("Rust: {rust}"),
|
2021-09-09 19:16:45 +01:00
|
|
|
diff::Result::Both(_, _) => (),
|
|
|
|
|
});
|
|
|
|
|
println!("****************************************************************");
|
|
|
|
|
}
|
|
|
|
|
FailureReason::RunC(intrinsic) => {
|
2022-10-25 15:17:23 -04:00
|
|
|
println!("Failed to run C program for intrinsic {intrinsic}")
|
2021-09-09 19:16:45 +01:00
|
|
|
}
|
|
|
|
|
FailureReason::RunRust(intrinsic) => {
|
2022-10-25 15:17:23 -04:00
|
|
|
println!("Failed to run rust program for intrinsic {intrinsic}")
|
2021-09-09 19:16:45 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
println!("{} differences found", intrinsics.len());
|
|
|
|
|
intrinsics.is_empty()
|
|
|
|
|
}
|