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:
Jacob Bramley 2023-07-28 15:09:46 +01:00 committed by Amanieu d'Antras
parent d2736197f7
commit 211a00769c
5 changed files with 132 additions and 63 deletions

View file

@ -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> {

View 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(())
}
}

View file

@ -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(),

View file

@ -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()
)
)
}

View file

@ -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)
)))
)