Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4520108

Browse files
committedJun 8, 2021
Intrinsic test tool to compare neon intrinsics with C
This tool generates rust and C programs that will call intrinsics and print the results with multiple different inputs. It will build all the programs and then run each of them and diff the output printing any differences that are found. It uses the tracking spreadsheet (with the % column renamed to enabled) to determine which intrinsics to test. It will filter out any intrinsics that have an argument with the name "n" or "lane" as those have constraints on them as to what numbers can be used.
1 parent 0087304 commit 4520108

File tree

9 files changed

+1280
-0
lines changed

9 files changed

+1280
-0
lines changed
 

‎.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ target
44
tags
55
crates/stdarch-gen/aarch64.rs
66
crates/stdarch-gen/arm.rs
7+
c_programs/*
8+
rust_programs/*

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ members = [
44
"crates/core_arch",
55
"crates/std_detect",
66
"crates/stdarch-gen",
7+
"crates/intrinsic-test",
78
"examples/"
89
]
910
exclude = [

‎crates/intrinsic-test/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "intrinsic-test"
3+
version = "0.1.0"
4+
authors = ["Jamie Cunliffe <Jamie.Cunliffe@arm.com>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
lazy_static = "1.4.0"
9+
serde = { version = "1", features = ["derive"] }
10+
csv = "1.1"
11+
clap = "2.33.3"
12+
regex = "1.4.2"
13+
log = "0.4.11"
14+
pretty_env_logger = "0.4.0"
15+
rayon = "1.5.0"
16+
diff = "0.1.12"

‎crates/intrinsic-test/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Generate and run programs using equivalent C and Rust intrinsics, checking that
2+
each produces the same result from random inputs.
3+
4+
# Usage
5+
```
6+
USAGE:
7+
intrinsic-test [OPTIONS] <INPUT>
8+
9+
FLAGS:
10+
-h, --help Prints help information
11+
-V, --version Prints version information
12+
13+
OPTIONS:
14+
--cppcompiler <CPPCOMPILER> The C++ compiler to use for compiling the c++ code [default: clang++]
15+
--toolchain <TOOLCHAIN> The rust toolchain to use for building the rust code [default: nightly]
16+
17+
ARGS:
18+
<INPUT> The input file containing the intrinsics
19+
```
20+
21+
The intrinsic.csv is the arm neon tracking google sheet (https://docs.google.com/spreadsheets/d/1MqW1g8c7tlhdRWQixgdWvR4uJHNZzCYAf4V0oHjZkwA/edit#gid=0)
22+
that contains the intrinsic list. The done percentage column should be renamed to "enabled".
23+

‎crates/intrinsic-test/src/argument.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use serde::{Deserialize, Deserializer};
2+
3+
use crate::types::IntrinsicType;
4+
use crate::Language;
5+
6+
/// An argument for the intrinsic.
7+
#[derive(Debug, PartialEq, Clone)]
8+
pub struct Argument {
9+
/// The argument's index in the intrinsic function call.
10+
pub pos: usize,
11+
/// The argument name.
12+
pub name: String,
13+
/// The type of the argument.
14+
pub ty: IntrinsicType,
15+
}
16+
17+
impl Argument {
18+
/// Creates an argument from a Rust style signature i.e. `name: type`
19+
fn from_rust(pos: usize, arg: &str) -> Result<Self, String> {
20+
let mut parts = arg.split(':');
21+
let name = parts.next().unwrap().trim().to_string();
22+
let ty = IntrinsicType::from_rust(parts.next().unwrap().trim())?;
23+
24+
Ok(Self { pos, name, ty })
25+
}
26+
27+
fn to_c_type(&self) -> String {
28+
self.ty.c_type()
29+
}
30+
31+
fn is_simd(&self) -> bool {
32+
self.ty.is_simd()
33+
}
34+
35+
pub fn is_ptr(&self) -> bool {
36+
self.ty.is_ptr()
37+
}
38+
}
39+
40+
#[derive(Debug, PartialEq, Clone)]
41+
pub struct ArgumentList {
42+
pub args: Vec<Argument>,
43+
}
44+
45+
impl ArgumentList {
46+
/// Creates an argument list from a Rust function signature, the data for
47+
/// this function should only be the arguments.
48+
/// e.g. for `fn test(a: u32, b: u32) -> u32` data should just be `a: u32, b: u32`
49+
fn from_rust_arguments(data: &str) -> Result<Self, String> {
50+
let args = data
51+
.split(',')
52+
.enumerate()
53+
.map(|(idx, arg)| Argument::from_rust(idx, arg))
54+
.collect::<Result<_, _>>()?;
55+
56+
Ok(Self { args })
57+
}
58+
59+
/// Converts the argument list into the call paramters for a C function call.
60+
/// e.g. this would generate something like `a, &b, c`
61+
pub fn as_call_param_c(&self) -> String {
62+
self.args
63+
.iter()
64+
.map(|arg| match arg.ty {
65+
IntrinsicType::Ptr { .. } => {
66+
format!("&{}", arg.name)
67+
}
68+
IntrinsicType::Type { .. } => arg.name.clone(),
69+
})
70+
.collect::<Vec<String>>()
71+
.join(", ")
72+
}
73+
74+
/// Converts the argument list into the call paramters for a Rust function.
75+
/// e.g. this would generate something like `a, b, c`
76+
pub fn as_call_param_rust(&self) -> String {
77+
self.args
78+
.iter()
79+
.map(|arg| arg.name.clone())
80+
.collect::<Vec<String>>()
81+
.join(", ")
82+
}
83+
84+
/// Creates a line that initializes this argument for C code.
85+
/// e.g. `int32x2_t a = { 0x1, 0x2 };`
86+
pub fn init_random_values_c(&self, pass: usize) -> String {
87+
self.iter()
88+
.map(|arg| {
89+
format!(
90+
"{ty} {name} = {{ {values} }};",
91+
ty = arg.to_c_type(),
92+
name = arg.name,
93+
values = arg.ty.populate_random(pass, &Language::C)
94+
)
95+
})
96+
.collect::<Vec<_>>()
97+
.join("\n ")
98+
}
99+
100+
/// Creates a line that initializes this argument for Rust code.
101+
/// e.g. `let a = transmute([0x1, 0x2]);`
102+
pub fn init_random_values_rust(&self, pass: usize) -> String {
103+
self.iter()
104+
.map(|arg| {
105+
if arg.is_simd() {
106+
format!(
107+
"let {name} = ::std::mem::transmute([{values}]);",
108+
name = arg.name,
109+
values = arg.ty.populate_random(pass, &Language::Rust),
110+
)
111+
} else {
112+
format!(
113+
"let {name} = {value};",
114+
name = arg.name,
115+
value = arg.ty.populate_random(pass, &Language::Rust)
116+
)
117+
}
118+
})
119+
.collect::<Vec<_>>()
120+
.join("\n ")
121+
}
122+
123+
pub fn iter(&self) -> std::slice::Iter<'_, Argument> {
124+
self.args.iter()
125+
}
126+
}
127+
128+
impl<'de> Deserialize<'de> for ArgumentList {
129+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130+
where
131+
D: Deserializer<'de>,
132+
{
133+
use serde::de::Error;
134+
let s = String::deserialize(deserializer)?;
135+
Self::from_rust_arguments(&s).map_err(Error::custom)
136+
}
137+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use crate::types::{IntrinsicType, TypeKind};
2+
3+
use super::argument::ArgumentList;
4+
use serde::de::Unexpected;
5+
use serde::{de, Deserialize, Deserializer};
6+
7+
/// An intrinsic
8+
#[derive(Deserialize, Debug, PartialEq, Clone)]
9+
pub struct Intrinsic {
10+
/// If the intrinsic should be tested.
11+
#[serde(deserialize_with = "bool_from_string")]
12+
pub enabled: bool,
13+
14+
/// The function name of this intrinsic.
15+
pub name: String,
16+
17+
/// Any arguments for this intrinsinc.
18+
#[serde(rename = "args")]
19+
pub arguments: ArgumentList,
20+
21+
/// The return type of this intrinsic.
22+
#[serde(rename = "return")]
23+
pub results: IntrinsicType,
24+
}
25+
26+
impl Intrinsic {
27+
/// Generates a std::cout for the intrinsics results that will match the
28+
/// rust debug output format for the return type.
29+
pub fn print_result_c(&self, index: usize) -> String {
30+
let lanes = if self.results.num_lanes() > 1 {
31+
(0..self.results.num_lanes())
32+
.map(|idx| -> std::string::String {
33+
format!(
34+
"{cast}{lane_fn}(__return_value, {lane})",
35+
cast = self.results.c_promotion(),
36+
lane_fn = self.results.get_lane_function(),
37+
lane = idx
38+
)
39+
})
40+
.collect::<Vec<_>>()
41+
.join(r#" << ", " << "#)
42+
} else {
43+
format!(
44+
"{promote}cast<{cast}>(__return_value)",
45+
cast = match self.results.kind() {
46+
TypeKind::Float if self.results.inner_size() == 32 => "float".to_string(),
47+
TypeKind::Float if self.results.inner_size() == 64 => "double".to_string(),
48+
TypeKind::Int => format!("int{}_t", self.results.inner_size()),
49+
TypeKind::UInt => format!("uint{}_t", self.results.inner_size()),
50+
TypeKind::Poly => format!("poly{}_t", self.results.inner_size()),
51+
ty => todo!("print_result_c - Unknown type: {:#?}", ty),
52+
},
53+
promote = self.results.c_promotion(),
54+
)
55+
};
56+
57+
format!(
58+
r#"std::cout << "Result {idx}: {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
59+
ty = if self.results.is_simd() {
60+
format!("{}(", self.results.c_type())
61+
} else {
62+
String::from("")
63+
},
64+
close = if self.results.is_simd() { ")" } else { "" },
65+
lanes = lanes,
66+
idx = index,
67+
)
68+
}
69+
70+
pub fn generate_pass_rust(&self, index: usize) -> String {
71+
format!(
72+
r#"
73+
unsafe {{
74+
{initialized_args}
75+
let res = {intrinsic_call}({args});
76+
println!("Result {idx}: {{:.150?}}", res);
77+
}}"#,
78+
initialized_args = self.arguments.init_random_values_rust(index),
79+
intrinsic_call = self.name,
80+
args = self.arguments.as_call_param_rust(),
81+
idx = index,
82+
)
83+
}
84+
85+
pub fn generate_pass_c(&self, index: usize) -> String {
86+
format!(
87+
r#" {{
88+
{initialized_args}
89+
auto __return_value = {intrinsic_call}({args});
90+
{print_result}
91+
}}"#,
92+
initialized_args = self.arguments.init_random_values_c(index),
93+
intrinsic_call = self.name,
94+
args = self.arguments.as_call_param_c(),
95+
print_result = self.print_result_c(index)
96+
)
97+
}
98+
}
99+
100+
fn bool_from_string<'de, D>(deserializer: D) -> Result<bool, D::Error>
101+
where
102+
D: Deserializer<'de>,
103+
{
104+
match String::deserialize(deserializer)?.to_uppercase().as_ref() {
105+
"TRUE" => Ok(true),
106+
"FALSE" => Ok(false),
107+
other => Err(de::Error::invalid_value(
108+
Unexpected::Str(other),
109+
&"TRUE or FALSE",
110+
)),
111+
}
112+
}

‎crates/intrinsic-test/src/main.rs

Lines changed: 380 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
#[macro_use]
2+
extern crate lazy_static;
3+
#[macro_use]
4+
extern crate log;
5+
6+
use std::fs::File;
7+
use std::io::Write;
8+
use std::process::Command;
9+
10+
use clap::{App, Arg};
11+
use intrinsic::Intrinsic;
12+
use rayon::prelude::*;
13+
use types::TypeKind;
14+
15+
mod argument;
16+
mod intrinsic;
17+
mod types;
18+
mod values;
19+
20+
#[derive(Debug, PartialEq)]
21+
pub enum Language {
22+
Rust,
23+
C,
24+
}
25+
26+
fn generate_c_program(header_file: &str, intrinsic: &Intrinsic) -> String {
27+
format!(
28+
r#"#include <{header_file}>
29+
#include <iostream>
30+
#include <cstring>
31+
#include <iomanip>
32+
#include <sstream>
33+
template<typename T1, typename T2> T1 cast(T2 x) {{
34+
static_assert(sizeof(T1) == sizeof(T2), "sizeof T1 and T2 must be the same");
35+
T1 ret = 0;
36+
memcpy(&ret, &x, sizeof(T1));
37+
return ret;
38+
}}
39+
std::ostream& operator<<(std::ostream& os, poly128_t value) {{
40+
std::stringstream temp;
41+
do {{
42+
int n = value % 10;
43+
value /= 10;
44+
temp << n;
45+
}} while (value != 0);
46+
std::string tempstr(temp.str());
47+
std::string res(tempstr.rbegin(), tempstr.rend());
48+
os << res;
49+
return os;
50+
}}
51+
int main(int argc, char **argv) {{
52+
{passes}
53+
return 0;
54+
}}"#,
55+
header_file = header_file,
56+
passes = (1..20)
57+
.map(|idx| intrinsic.generate_pass_c(idx))
58+
.collect::<Vec<_>>()
59+
.join("\n"),
60+
)
61+
}
62+
63+
fn generate_rust_program(intrinsic: &Intrinsic) -> String {
64+
format!(
65+
r#"#![feature(simd_ffi)]
66+
#![feature(link_llvm_intrinsics)]
67+
#![feature(stdsimd)]
68+
#![allow(overflowing_literals)]
69+
use core::arch::aarch64::*;
70+
#[allow(unused_macros)]
71+
macro_rules! bits_to_float(
72+
($t:ty, $bits:expr) => (
73+
{{ let x: $t = ::std::mem::transmute($bits); x }}
74+
)
75+
);
76+
77+
fn main() {{
78+
{passes}
79+
}}
80+
"#,
81+
passes = (1..20)
82+
.map(|idx| intrinsic.generate_pass_rust(idx))
83+
.collect::<Vec<_>>()
84+
.join("\n"),
85+
)
86+
}
87+
88+
fn compile_c(c_filename: &str, intrinsic: &Intrinsic, compiler: &str) -> bool {
89+
let output = Command::new("sh")
90+
.arg("-c")
91+
.arg(format!(
92+
"{cpp} -Wno-narrowing -O2 -target {target} -o c_programs/{intrinsic} {filename}",
93+
target = "aarch64-unknown-linux-gnu",
94+
filename = c_filename,
95+
intrinsic = intrinsic.name,
96+
cpp = compiler,
97+
))
98+
.output();
99+
if let Ok(output) = output {
100+
if output.status.success() {
101+
true
102+
} else {
103+
let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
104+
if stderr.contains("error: use of undeclared identifier") {
105+
warn!("Skipping intrinsic due to no support: {}", intrinsic.name);
106+
true
107+
} else {
108+
error!(
109+
"Failed to compile code for intrinsic: {}\n\nstdout:\n{}\n\nstderr:\n{}",
110+
intrinsic.name,
111+
std::str::from_utf8(&output.stdout).unwrap_or(""),
112+
std::str::from_utf8(&output.stderr).unwrap_or("")
113+
);
114+
false
115+
}
116+
}
117+
} else {
118+
error!("Command failed: {:#?}", output);
119+
false
120+
}
121+
}
122+
123+
fn build_c(intrinsics: &Vec<Intrinsic>, compiler: &str) -> bool {
124+
let _ = std::fs::create_dir("c_programs");
125+
intrinsics
126+
.par_iter()
127+
.map(|i| {
128+
let c_filename = format!(r#"c_programs/{}.cpp"#, i.name);
129+
let mut file = File::create(&c_filename).unwrap();
130+
131+
let c_code = generate_c_program("arm_neon.h", &i);
132+
file.write_all(c_code.into_bytes().as_slice()).unwrap();
133+
compile_c(&c_filename, &i, compiler)
134+
})
135+
.find_any(|x| !x)
136+
.is_none()
137+
}
138+
139+
fn build_rust(intrinsics: &Vec<Intrinsic>, toolchain: &str) -> bool {
140+
intrinsics.iter().for_each(|i| {
141+
let rust_dir = format!(r#"rust_programs/{}"#, i.name);
142+
let _ = std::fs::create_dir_all(&rust_dir);
143+
let rust_filename = format!(r#"{}/main.rs"#, rust_dir);
144+
let mut file = File::create(&rust_filename).unwrap();
145+
146+
let c_code = generate_rust_program(&i);
147+
file.write_all(c_code.into_bytes().as_slice()).unwrap();
148+
});
149+
150+
let mut cargo = File::create("rust_programs/Cargo.toml").unwrap();
151+
cargo
152+
.write_all(
153+
format!(
154+
r#"[package]
155+
name = "intrinsic-test"
156+
version = "{version}"
157+
authors = ["{authors}"]
158+
edition = "2018"
159+
[workspace]
160+
{binaries}"#,
161+
version = env!("CARGO_PKG_VERSION"),
162+
authors = env!("CARGO_PKG_AUTHORS"),
163+
binaries = intrinsics
164+
.iter()
165+
.map(|i| {
166+
format!(
167+
r#"[[bin]]
168+
name = "{intrinsic}"
169+
path = "{intrinsic}/main.rs""#,
170+
intrinsic = i.name
171+
)
172+
})
173+
.collect::<Vec<_>>()
174+
.join("\n")
175+
)
176+
.into_bytes()
177+
.as_slice(),
178+
)
179+
.unwrap();
180+
181+
let output = Command::new("sh")
182+
.current_dir("rust_programs")
183+
.arg("-c")
184+
.arg(format!(
185+
"cargo +{toolchain} build --release",
186+
toolchain = toolchain,
187+
))
188+
.output();
189+
if let Ok(output) = output {
190+
if output.status.success() {
191+
true
192+
} else {
193+
error!(
194+
"Failed to compile code for intrinsics\n\nstdout:\n{}\n\nstderr:\n{}",
195+
std::str::from_utf8(&output.stdout).unwrap_or(""),
196+
std::str::from_utf8(&output.stderr).unwrap_or("")
197+
);
198+
false
199+
}
200+
} else {
201+
error!("Command failed: {:#?}", output);
202+
false
203+
}
204+
}
205+
206+
fn main() {
207+
pretty_env_logger::init();
208+
209+
let matches = App::new("Intrinsic test tool")
210+
.about("Generates Rust and C programs for intrinsics and compares the output")
211+
.arg(
212+
Arg::with_name("INPUT")
213+
.help("The input file containing the intrinsics")
214+
.required(true)
215+
.index(1),
216+
)
217+
.arg(
218+
Arg::with_name("TOOLCHAIN")
219+
.takes_value(true)
220+
.default_value("nightly")
221+
.long("toolchain")
222+
.help("The rust toolchain to use for building the rust code"),
223+
)
224+
.arg(
225+
Arg::with_name("CPPCOMPILER")
226+
.takes_value(true)
227+
.default_value("clang++")
228+
.long("cppcompiler")
229+
.help("The C++ compiler to use for compiling the c++ code"),
230+
)
231+
.get_matches();
232+
233+
let filename = matches.value_of("INPUT").unwrap();
234+
let toolchain = matches.value_of("TOOLCHAIN").unwrap();
235+
let cpp_compiler = matches.value_of("CPPCOMPILER").unwrap();
236+
237+
let mut csv_reader = csv::Reader::from_reader(std::fs::File::open(filename).unwrap());
238+
239+
let mut intrinsics = csv_reader
240+
.deserialize()
241+
.filter_map(|x| -> Option<Intrinsic> {
242+
debug!("Processing {:#?}", x);
243+
match x {
244+
Ok(a) => Some(a),
245+
e => {
246+
error!("{:#?}", e);
247+
None
248+
}
249+
}
250+
})
251+
// Only perform the test for intrinsics that are enabled...
252+
.filter(|i| i.enabled)
253+
// Not sure how we would compare intrinsic that returns void.
254+
.filter(|i| i.results.kind() != TypeKind::Void)
255+
.filter(|i| i.results.kind() != TypeKind::BFloat)
256+
.filter(|i| !(i.results.kind() == TypeKind::Float && i.results.inner_size() == 16))
257+
.filter(|i| {
258+
i.arguments
259+
.iter()
260+
.find(|a| a.ty.kind() == TypeKind::BFloat)
261+
.is_none()
262+
})
263+
.filter(|i| {
264+
i.arguments
265+
.iter()
266+
.find(|a| a.ty.kind() == TypeKind::Float && a.ty.inner_size() == 16)
267+
.is_none()
268+
})
269+
// Skip pointers for now, we would probably need to look at the return
270+
// type to work out how many elements we need to point to.
271+
.filter(|i| i.arguments.iter().find(|a| a.is_ptr()).is_none())
272+
// intrinsics with a lane parameter have constraints, deal with them later.
273+
.filter(|i| {
274+
i.arguments
275+
.iter()
276+
.find(|a| a.name.starts_with("lane"))
277+
.is_none()
278+
})
279+
.filter(|i| i.arguments.iter().find(|a| a.name == "n").is_none())
280+
.collect::<Vec<_>>();
281+
intrinsics.dedup();
282+
283+
if !build_c(&intrinsics, cpp_compiler) {
284+
std::process::exit(2);
285+
}
286+
287+
if !build_rust(&intrinsics, &toolchain) {
288+
std::process::exit(3);
289+
}
290+
291+
if !compare_outputs(&intrinsics, &toolchain) {
292+
std::process::exit(1)
293+
}
294+
}
295+
296+
enum FailureReason {
297+
RunC(String),
298+
RunRust(String),
299+
Difference(String, String, String),
300+
}
301+
302+
fn compare_outputs(intrinsics: &Vec<Intrinsic>, toolchain: &str) -> bool {
303+
let intrinsics = intrinsics
304+
.par_iter()
305+
.filter_map(|intrinsic| {
306+
let c = Command::new("sh")
307+
.arg("-c")
308+
.arg(format!(
309+
"./c_programs/{intrinsic}",
310+
intrinsic = intrinsic.name,
311+
))
312+
.output();
313+
let rust = Command::new("sh")
314+
.current_dir("rust_programs")
315+
.arg("-c")
316+
.arg(format!(
317+
"cargo +{toolchain} run --release --bin {intrinsic}",
318+
intrinsic = intrinsic.name,
319+
toolchain = toolchain,
320+
))
321+
.output();
322+
323+
let (c, rust) = match (c, rust) {
324+
(Ok(c), Ok(rust)) => (c, rust),
325+
a => panic!("{:#?}", a),
326+
};
327+
328+
if !c.status.success() {
329+
error!("Failed to run C program for intrinsic {}", intrinsic.name);
330+
return Some(FailureReason::RunC(intrinsic.name.clone()));
331+
}
332+
333+
if !rust.status.success() {
334+
error!(
335+
"Failed to run rust program for intrinsic {}",
336+
intrinsic.name
337+
);
338+
return Some(FailureReason::RunRust(intrinsic.name.clone()));
339+
}
340+
341+
info!("Comparing intrinsic: {}", intrinsic.name);
342+
343+
let c = std::str::from_utf8(&c.stdout)
344+
.unwrap()
345+
.to_lowercase()
346+
.replace("-nan", "nan");
347+
let rust = std::str::from_utf8(&rust.stdout)
348+
.unwrap()
349+
.to_lowercase()
350+
.replace("-nan", "nan");
351+
352+
if c == rust {
353+
None
354+
} else {
355+
Some(FailureReason::Difference(intrinsic.name.clone(), c, rust))
356+
}
357+
})
358+
.collect::<Vec<_>>();
359+
360+
intrinsics.iter().for_each(|reason| match reason {
361+
FailureReason::Difference(intrinsic, c, rust) => {
362+
println!("Difference for intrinsic: {}", intrinsic);
363+
let diff = diff::lines(c, rust);
364+
diff.iter().for_each(|diff| match diff {
365+
diff::Result::Left(c) => println!("C: {}", c),
366+
diff::Result::Right(rust) => println!("Rust: {}", rust),
367+
diff::Result::Both(_, _) => (),
368+
});
369+
println!("****************************************************************");
370+
}
371+
FailureReason::RunC(intrinsic) => {
372+
println!("Failed to run C program for intrinsic {}", intrinsic)
373+
}
374+
FailureReason::RunRust(intrinsic) => {
375+
println!("Failed to run rust program for intrinsic {}", intrinsic)
376+
}
377+
});
378+
println!("{} differences found", intrinsics.len());
379+
intrinsics.is_empty()
380+
}

‎crates/intrinsic-test/src/types.rs

Lines changed: 483 additions & 0 deletions
Large diffs are not rendered by default.

‎crates/intrinsic-test/src/values.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/// Gets a hex constant value for a single lane in in a determistic way
2+
/// * `bits`: The number of bits for the type, only 8, 16, 32, 64 are valid values
3+
/// * `simd`: The index of the simd lane we are generating for
4+
/// * `pass`: The index of the pass we are generating the values for
5+
pub fn values_for_pass(bits: u32, simd: u32, pass: usize) -> String {
6+
let index = pass + (simd as usize);
7+
8+
if bits == 8 {
9+
format!("{:#X}", VALUES_8[index % VALUES_8.len()])
10+
} else if bits == 16 {
11+
format!("{:#X}", VALUES_16[index % VALUES_16.len()])
12+
} else if bits == 32 {
13+
format!("{:#X}", VALUES_32[index % VALUES_32.len()])
14+
} else if bits == 64 {
15+
format!("{:#X}", VALUES_64[index % VALUES_64.len()])
16+
} else {
17+
panic!("Unknown size: {}", bits);
18+
}
19+
}
20+
21+
pub const VALUES_8: &[u8] = &[
22+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
23+
0xf0, 0x80, 0x3b, 0xff,
24+
];
25+
26+
pub const VALUES_16: &[u16] = &[
27+
0x0000, // 0.0
28+
0x0400, // The smallest normal value.
29+
0x37ff, // The value just below 0.5.
30+
0x3800, // 0.5
31+
0x3801, // The value just above 0.5.
32+
0x3bff, // The value just below 1.0.
33+
0x3c00, // 1.0
34+
0x3c01, // The value just above 1.0.
35+
0x3e00, // 1.5
36+
0x4900, // 10
37+
0x7bff, // The largest finite value.
38+
0x7c00, // Infinity.
39+
// NaNs.
40+
// - Quiet NaNs
41+
0x7f23, 0x7e00, // - Signalling NaNs
42+
0x7d23, 0x7c01, // Subnormals.
43+
// - A recognisable bit pattern.
44+
0x0012, // - The largest subnormal value.
45+
0x03ff, // - The smallest subnormal value.
46+
0x0001, // The same values again, but negated.
47+
0x8000, 0x8400, 0xb7ff, 0xb800, 0xb801, 0xbbff, 0xbc00, 0xbc01, 0xbe00, 0xc900, 0xfbff, 0xfc00,
48+
0xff23, 0xfe00, 0xfd23, 0xfc01, 0x8012, 0x83ff, 0x8001,
49+
];
50+
51+
pub const VALUES_32: &[u32] = &[
52+
// Simple values.
53+
0x00000000, // 0.0
54+
0x00800000, // The smallest normal value.
55+
0x3effffff, // The value just below 0.5.
56+
0x3f000000, // 0.5
57+
0x3f000001, // The value just above 0.5.
58+
0x3f7fffff, // The value just below 1.0.
59+
0x3f800000, // 1.0
60+
0x3f800001, // The value just above 1.0.
61+
0x3fc00000, // 1.5
62+
0x41200000, // 10
63+
0x7f8fffff, // The largest finite value.
64+
0x7f800000, // Infinity.
65+
// NaNs.
66+
// - Quiet NaNs
67+
0x7fd23456, 0x7fc00000, // - Signalling NaNs
68+
0x7f923456, 0x7f800001, // Subnormals.
69+
// - A recognisable bit pattern.
70+
0x00123456, // - The largest subnormal value.
71+
0x007fffff, // - The smallest subnormal value.
72+
0x00000001, // The same values again, but negated.
73+
0x80000000, 0x80800000, 0xbeffffff, 0xbf000000, 0xbf000001, 0xbf7fffff, 0xbf800000, 0xbf800001,
74+
0xbfc00000, 0xc1200000, 0xff8fffff, 0xff800000, 0xffd23456, 0xffc00000, 0xff923456, 0xff800001,
75+
0x80123456, 0x807fffff, 0x80000001,
76+
];
77+
78+
pub const VALUES_64: &[u64] = &[
79+
// Simple values.
80+
0x0000000000000000, // 0.0
81+
0x0010000000000000, // The smallest normal value.
82+
0x3fdfffffffffffff, // The value just below 0.5.
83+
0x3fe0000000000000, // 0.5
84+
0x3fe0000000000001, // The value just above 0.5.
85+
0x3fefffffffffffff, // The value just below 1.0.
86+
0x3ff0000000000000, // 1.0
87+
0x3ff0000000000001, // The value just above 1.0.
88+
0x3ff8000000000000, // 1.5
89+
0x4024000000000000, // 10
90+
0x7fefffffffffffff, // The largest finite value.
91+
0x7ff0000000000000, // Infinity.
92+
// NaNs.
93+
// - Quiet NaNs
94+
0x7ff923456789abcd,
95+
0x7ff8000000000000,
96+
// - Signalling NaNs
97+
0x7ff123456789abcd,
98+
0x7ff0000000000000,
99+
// Subnormals.
100+
// - A recognisable bit pattern.
101+
0x000123456789abcd,
102+
// - The largest subnormal value.
103+
0x000fffffffffffff,
104+
// - The smallest subnormal value.
105+
0x0000000000000001,
106+
// The same values again, but negated.
107+
0x8000000000000000,
108+
0x8010000000000000,
109+
0xbfdfffffffffffff,
110+
0xbfe0000000000000,
111+
0xbfe0000000000001,
112+
0xbfefffffffffffff,
113+
0xbff0000000000000,
114+
0xbff0000000000001,
115+
0xbff8000000000000,
116+
0xc024000000000000,
117+
0xffefffffffffffff,
118+
0xfff0000000000000,
119+
0xfff923456789abcd,
120+
0xfff8000000000000,
121+
0xfff123456789abcd,
122+
0xfff0000000000000,
123+
0x800123456789abcd,
124+
0x800fffffffffffff,
125+
0x8000000000000001,
126+
];

0 commit comments

Comments
 (0)
Please sign in to comment.