Improve intrinsic-test output formatting.
This change is simple, but makes the generated tests much easier to follow (and debug).
This commit is contained in:
parent
d2736197f7
commit
211a00769c
5 changed files with 132 additions and 63 deletions
|
|
@ -1,5 +1,6 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use crate::format::Indentation;
|
||||
use crate::json_parser::ArgPrep;
|
||||
use crate::types::{IntrinsicType, TypeKind};
|
||||
use crate::Language;
|
||||
|
|
@ -169,15 +170,15 @@ impl ArgumentList {
|
|||
/// Creates a line for each argument that initializes an array for C from which `loads` argument
|
||||
/// values can be loaded as a sliding window.
|
||||
/// e.g `const int32x2_t a_vals = {0x3effffff, 0x3effffff, 0x3f7fffff}`, if loads=2.
|
||||
pub fn gen_arglists_c(&self, loads: u32) -> String {
|
||||
pub fn gen_arglists_c(&self, indentation: Indentation, loads: u32) -> String {
|
||||
self.iter()
|
||||
.filter_map(|arg| {
|
||||
(!arg.has_constraint()).then(|| {
|
||||
format!(
|
||||
"const {ty} {name}_vals[] = {values};",
|
||||
"{indentation}const {ty} {name}_vals[] = {values};",
|
||||
ty = arg.ty.c_scalar_type(),
|
||||
name = arg.name,
|
||||
values = arg.ty.populate_random(loads, &Language::C)
|
||||
values = arg.ty.populate_random(indentation, loads, &Language::C)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -187,17 +188,17 @@ impl ArgumentList {
|
|||
|
||||
/// Creates a line for each argument that initializes an array for Rust from which `loads` argument
|
||||
/// values can be loaded as a sliding window, e.g `const A_VALS: [u32; 20] = [...];`
|
||||
pub fn gen_arglists_rust(&self, loads: u32) -> String {
|
||||
pub fn gen_arglists_rust(&self, indentation: Indentation, loads: u32) -> String {
|
||||
self.iter()
|
||||
.filter_map(|arg| {
|
||||
(!arg.has_constraint()).then(|| {
|
||||
format!(
|
||||
"{bind} {name}: [{ty}; {load_size}] = {values};",
|
||||
"{indentation}{bind} {name}: [{ty}; {load_size}] = {values};",
|
||||
bind = arg.rust_vals_array_binding(),
|
||||
name = arg.rust_vals_array_name(),
|
||||
ty = arg.ty.rust_scalar_type(),
|
||||
load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1,
|
||||
values = arg.ty.populate_random(loads, &Language::Rust)
|
||||
values = arg.ty.populate_random(indentation, loads, &Language::Rust)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -208,7 +209,7 @@ impl ArgumentList {
|
|||
/// Creates a line for each argument that initializes the argument from an array `[arg]_vals` at
|
||||
/// an offset `i` using a load intrinsic, in C.
|
||||
/// e.g `uint8x8_t a = vld1_u8(&a_vals[i]);`
|
||||
pub fn load_values_c(&self, p64_armv7_workaround: bool) -> String {
|
||||
pub fn load_values_c(&self, indentation: Indentation, p64_armv7_workaround: bool) -> String {
|
||||
self.iter()
|
||||
.filter_map(|arg| {
|
||||
// The ACLE doesn't support 64-bit polynomial loads on Armv7
|
||||
|
|
@ -221,7 +222,7 @@ impl ArgumentList {
|
|||
|
||||
(!arg.has_constraint()).then(|| {
|
||||
format!(
|
||||
"{ty} {name} = {open_cast}{load}(&{name}_vals[i]){close_cast};",
|
||||
"{indentation}{ty} {name} = {open_cast}{load}(&{name}_vals[i]){close_cast};\n",
|
||||
ty = arg.to_c_type(),
|
||||
name = arg.name,
|
||||
load = if arg.is_simd() {
|
||||
|
|
@ -242,19 +243,18 @@ impl ArgumentList {
|
|||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Creates a line for each argument that initializes the argument from array `[ARG]_VALS` at
|
||||
/// an offset `i` using a load intrinsic, in Rust.
|
||||
/// e.g `let a = vld1_u8(A_VALS.as_ptr().offset(i));`
|
||||
pub fn load_values_rust(&self) -> String {
|
||||
pub fn load_values_rust(&self, indentation: Indentation) -> String {
|
||||
self.iter()
|
||||
.filter_map(|arg| {
|
||||
(!arg.has_constraint()).then(|| {
|
||||
format!(
|
||||
"let {name} = {load}({vals_name}.as_ptr().offset(i));",
|
||||
"{indentation}let {name} = {load}({vals_name}.as_ptr().offset(i));\n",
|
||||
name = arg.name,
|
||||
vals_name = arg.rust_vals_array_name(),
|
||||
load = if arg.is_simd() {
|
||||
|
|
@ -265,8 +265,7 @@ impl ArgumentList {
|
|||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n ")
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, Argument> {
|
||||
|
|
|
|||
28
library/stdarch/crates/intrinsic-test/src/format.rs
Normal file
28
library/stdarch/crates/intrinsic-test/src/format.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//! Basic code formatting tools.
|
||||
//!
|
||||
//! We don't need perfect formatting for the generated tests, but simple indentation can make
|
||||
//! debugging a lot easier.
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Indentation(u32);
|
||||
|
||||
impl std::default::Default for Indentation {
|
||||
fn default() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Indentation {
|
||||
pub fn nested(self) -> Self {
|
||||
Self(self.0 + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Indentation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for _ in 0..self.0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::format::Indentation;
|
||||
use crate::types::{IntrinsicType, TypeKind};
|
||||
|
||||
use super::argument::ArgumentList;
|
||||
|
|
@ -22,7 +23,7 @@ impl Intrinsic {
|
|||
/// Generates a std::cout for the intrinsics results that will match the
|
||||
/// rust debug output format for the return type. The generated line assumes
|
||||
/// there is an int i in scope which is the current pass number.
|
||||
pub fn print_result_c(&self, additional: &str) -> String {
|
||||
pub fn print_result_c(&self, indentation: Indentation, additional: &str) -> String {
|
||||
let lanes = if self.results.num_vectors() > 1 {
|
||||
(0..self.results.num_vectors())
|
||||
.map(|vector| {
|
||||
|
|
@ -73,7 +74,7 @@ impl Intrinsic {
|
|||
};
|
||||
|
||||
format!(
|
||||
r#"std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
|
||||
r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
|
||||
ty = if self.results.is_simd() {
|
||||
format!("{}(", self.results.c_type())
|
||||
} else {
|
||||
|
|
@ -87,26 +88,33 @@ impl Intrinsic {
|
|||
|
||||
pub fn generate_loop_c(
|
||||
&self,
|
||||
indentation: Indentation,
|
||||
additional: &str,
|
||||
passes: u32,
|
||||
p64_armv7_workaround: bool,
|
||||
) -> String {
|
||||
let body_indentation = indentation.nested();
|
||||
format!(
|
||||
r#" {{
|
||||
for (int i=0; i<{passes}; i++) {{
|
||||
{loaded_args}
|
||||
auto __return_value = {intrinsic_call}({args});
|
||||
{print_result}
|
||||
}}
|
||||
}}"#,
|
||||
loaded_args = self.arguments.load_values_c(p64_armv7_workaround),
|
||||
"{indentation}for (int i=0; i<{passes}; i++) {{\n\
|
||||
{loaded_args}\
|
||||
{body_indentation}auto __return_value = {intrinsic_call}({args});\n\
|
||||
{print_result}\n\
|
||||
{indentation}}}",
|
||||
loaded_args = self
|
||||
.arguments
|
||||
.load_values_c(body_indentation, p64_armv7_workaround),
|
||||
intrinsic_call = self.name,
|
||||
args = self.arguments.as_call_param_c(),
|
||||
print_result = self.print_result_c(additional)
|
||||
print_result = self.print_result_c(body_indentation, additional)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn generate_loop_rust(&self, additional: &str, passes: u32) -> String {
|
||||
pub fn generate_loop_rust(
|
||||
&self,
|
||||
indentation: Indentation,
|
||||
additional: &str,
|
||||
passes: u32,
|
||||
) -> String {
|
||||
let constraints = self.arguments.as_constraint_parameters_rust();
|
||||
let constraints = if !constraints.is_empty() {
|
||||
format!("::<{constraints}>")
|
||||
|
|
@ -114,17 +122,17 @@ impl Intrinsic {
|
|||
constraints
|
||||
};
|
||||
|
||||
let indentation2 = indentation.nested();
|
||||
let indentation3 = indentation2.nested();
|
||||
format!(
|
||||
r#" {{
|
||||
for i in 0..{passes} {{
|
||||
unsafe {{
|
||||
{loaded_args}
|
||||
let __return_value = {intrinsic_call}{const}({args});
|
||||
println!("Result {additional}-{{}}: {{:.150?}}", i+1, __return_value);
|
||||
}}
|
||||
}}
|
||||
}}"#,
|
||||
loaded_args = self.arguments.load_values_rust(),
|
||||
"{indentation}for i in 0..{passes} {{\n\
|
||||
{indentation2}unsafe {{\n\
|
||||
{loaded_args}\
|
||||
{indentation3}let __return_value = {intrinsic_call}{const}({args});\n\
|
||||
{indentation3}println!(\"Result {additional}-{{}}: {{:.150?}}\", i + 1, __return_value);\n\
|
||||
{indentation2}}}\n\
|
||||
{indentation}}}",
|
||||
loaded_args = self.arguments.load_values_rust(indentation3),
|
||||
intrinsic_call = self.name,
|
||||
const = constraints,
|
||||
args = self.arguments.as_call_param_rust(),
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ use rayon::prelude::*;
|
|||
use types::TypeKind;
|
||||
|
||||
use crate::argument::Argument;
|
||||
use crate::format::Indentation;
|
||||
use crate::json_parser::get_neon_intrinsics;
|
||||
|
||||
mod argument;
|
||||
mod format;
|
||||
mod intrinsic;
|
||||
mod json_parser;
|
||||
mod types;
|
||||
|
|
@ -31,6 +33,7 @@ pub enum Language {
|
|||
}
|
||||
|
||||
fn gen_code_c(
|
||||
indentation: Indentation,
|
||||
intrinsic: &Intrinsic,
|
||||
constraints: &[&Argument],
|
||||
name: String,
|
||||
|
|
@ -43,17 +46,19 @@ fn gen_code_c(
|
|||
.map(|c| c.to_range())
|
||||
.flat_map(|r| r.into_iter());
|
||||
|
||||
let body_indentation = indentation.nested();
|
||||
range
|
||||
.map(|i| {
|
||||
format!(
|
||||
r#" {{
|
||||
{ty} {name} = {val};
|
||||
{pass}
|
||||
}}"#,
|
||||
"{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}"),
|
||||
|
|
@ -61,9 +66,9 @@ fn gen_code_c(
|
|||
)
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
.join("\n")
|
||||
} else {
|
||||
intrinsic.generate_loop_c(&name, PASSES, p64_armv7_workaround)
|
||||
intrinsic.generate_loop_c(indentation, &name, PASSES, p64_armv7_workaround)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +84,7 @@ fn generate_c_program(
|
|||
.filter(|i| i.has_constraint())
|
||||
.collect_vec();
|
||||
|
||||
let indentation = Indentation::default();
|
||||
format!(
|
||||
r#"{notices}{header_files}
|
||||
#include <iostream>
|
||||
|
|
@ -119,8 +125,9 @@ int main(int argc, char **argv) {{
|
|||
.map(|header| format!("#include <{header}>"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
arglists = intrinsic.arguments.gen_arglists_c(PASSES),
|
||||
arglists = intrinsic.arguments.gen_arglists_c(indentation, PASSES),
|
||||
passes = gen_code_c(
|
||||
indentation.nested(),
|
||||
intrinsic,
|
||||
constraints.as_slice(),
|
||||
Default::default(),
|
||||
|
|
@ -129,7 +136,12 @@ int main(int argc, char **argv) {{
|
|||
)
|
||||
}
|
||||
|
||||
fn gen_code_rust(intrinsic: &Intrinsic, constraints: &[&Argument], name: String) -> String {
|
||||
fn gen_code_rust(
|
||||
indentation: Indentation,
|
||||
intrinsic: &Intrinsic,
|
||||
constraints: &[&Argument],
|
||||
name: String,
|
||||
) -> String {
|
||||
if let Some((current, constraints)) = constraints.split_last() {
|
||||
let range = current
|
||||
.constraints
|
||||
|
|
@ -137,22 +149,28 @@ fn gen_code_rust(intrinsic: &Intrinsic, constraints: &[&Argument], name: String)
|
|||
.map(|c| c.to_range())
|
||||
.flat_map(|r| r.into_iter());
|
||||
|
||||
let body_indentation = indentation.nested();
|
||||
range
|
||||
.map(|i| {
|
||||
format!(
|
||||
r#" {{
|
||||
const {name}: {ty} = {val};
|
||||
{pass}
|
||||
}}"#,
|
||||
"{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(intrinsic, constraints, format!("{name}-{i}"))
|
||||
pass = gen_code_rust(
|
||||
body_indentation,
|
||||
intrinsic,
|
||||
constraints,
|
||||
format!("{name}-{i}")
|
||||
)
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
.join("\n")
|
||||
} else {
|
||||
intrinsic.generate_loop_rust(&name, PASSES)
|
||||
intrinsic.generate_loop_rust(indentation, &name, PASSES)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +181,7 @@ fn generate_rust_program(notices: &str, intrinsic: &Intrinsic, a32: bool) -> Str
|
|||
.filter(|i| i.has_constraint())
|
||||
.collect_vec();
|
||||
|
||||
let indentation = Indentation::default();
|
||||
format!(
|
||||
r#"{notices}#![feature(simd_ffi)]
|
||||
#![feature(link_llvm_intrinsics)]
|
||||
|
|
@ -183,8 +202,15 @@ fn main() {{
|
|||
}}
|
||||
"#,
|
||||
target_arch = if a32 { "arm" } else { "aarch64" },
|
||||
arglists = intrinsic.arguments.gen_arglists_rust(PASSES),
|
||||
passes = gen_code_rust(intrinsic, &constraints, Default::default())
|
||||
arglists = intrinsic
|
||||
.arguments
|
||||
.gen_arglists_rust(indentation.nested(), PASSES),
|
||||
passes = gen_code_rust(
|
||||
indentation.nested(),
|
||||
intrinsic,
|
||||
&constraints,
|
||||
Default::default()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::str::FromStr;
|
|||
|
||||
use itertools::Itertools as _;
|
||||
|
||||
use crate::format::Indentation;
|
||||
use crate::values::value_for_array;
|
||||
use crate::Language;
|
||||
|
||||
|
|
@ -301,9 +302,14 @@ impl IntrinsicType {
|
|||
/// Returns a string such as
|
||||
/// * `{0x1, 0x7F, 0xFF}` if `language` is `Language::C`
|
||||
/// * `[0x1 as _, 0x7F as _, 0xFF as _]` if `language` is `Language::Rust`
|
||||
pub fn populate_random(&self, loads: u32, language: &Language) -> String {
|
||||
pub fn populate_random(
|
||||
&self,
|
||||
indentation: Indentation,
|
||||
loads: u32,
|
||||
language: &Language,
|
||||
) -> String {
|
||||
match self {
|
||||
IntrinsicType::Ptr { child, .. } => child.populate_random(loads, language),
|
||||
IntrinsicType::Ptr { child, .. } => child.populate_random(indentation, loads, language),
|
||||
IntrinsicType::Type {
|
||||
bit_len: Some(bit_len @ (8 | 16 | 32 | 64)),
|
||||
kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly),
|
||||
|
|
@ -315,10 +321,11 @@ impl IntrinsicType {
|
|||
&Language::Rust => ("[", "]"),
|
||||
&Language::C => ("{", "}"),
|
||||
};
|
||||
let body_indentation = indentation.nested();
|
||||
format!(
|
||||
"{prefix}{body}{suffix}",
|
||||
"{prefix}\n{body}\n{indentation}{suffix}",
|
||||
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
|
||||
.format_with(", ", |i, fmt| {
|
||||
.format_with(",\n", |i, fmt| {
|
||||
let src = value_for_array(*bit_len, i);
|
||||
assert!(src == 0 || src.ilog2() < *bit_len);
|
||||
if *kind == TypeKind::Int && (src >> (*bit_len - 1)) != 0 {
|
||||
|
|
@ -329,12 +336,12 @@ impl IntrinsicType {
|
|||
if (twos_compl == src) && (language == &Language::C) {
|
||||
// `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid
|
||||
// undefined literal overflow behaviour.
|
||||
fmt(&format_args!("-{ones_compl:#x} - 1"))
|
||||
fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1"))
|
||||
} else {
|
||||
fmt(&format_args!("-{twos_compl:#x}"))
|
||||
fmt(&format_args!("{body_indentation}-{twos_compl:#x}"))
|
||||
}
|
||||
} else {
|
||||
fmt(&format_args!("{src:#x}"))
|
||||
fmt(&format_args!("{body_indentation}{src:#x}"))
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
@ -354,10 +361,11 @@ impl IntrinsicType {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
format!(
|
||||
"{prefix}{body}{suffix}",
|
||||
"{prefix}\n{body}\n{indentation}{suffix}",
|
||||
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
|
||||
.format_with(", ", |i, fmt| fmt(&format_args!(
|
||||
"{cast_prefix}{src:#x}{cast_suffix}",
|
||||
.format_with(",\n", |i, fmt| fmt(&format_args!(
|
||||
"{indentation}{cast_prefix}{src:#x}{cast_suffix}",
|
||||
indentation = indentation.nested(),
|
||||
src = value_for_array(*bit_len, i)
|
||||
)))
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue