Skip to content

Change test tool to use a loop and load intrinsic. #1318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions crates/intrinsic-test/missing_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,6 @@ vrnd64xq_f64
vrnd64z_f64
vrnd64zq_f64

# Takes too long to compile tests
vcopyq_laneq_u8
vcopyq_laneq_s8
vcopyq_laneq_p8
vcopyq_lane_u8
vcopyq_lane_s8
vcopyq_lane_p8
vcopy_laneq_u8
vcopy_laneq_s8
vcopy_laneq_p8
vcopy_lane_u8
vcopy_lane_s8
vcopy_lane_p8

# QEMU 6.0 doesn't support these instructions
vmmlaq_s32
vmmlaq_u32
Expand Down
107 changes: 83 additions & 24 deletions crates/intrinsic-test/src/argument.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::Range;

use crate::types::IntrinsicType;
use crate::types::{IntrinsicType, TypeKind};
use crate::Language;

/// An argument for the intrinsic.
Expand Down Expand Up @@ -90,49 +90,108 @@ impl ArgumentList {
.join(", ")
}

/// Creates a line that initializes this argument for C code.
/// e.g. `int32x2_t a = { 0x1, 0x2 };`
pub fn init_random_values_c(&self, pass: usize) -> String {
/// 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 {
self.iter()
.filter_map(|arg| {
(!arg.has_constraint()).then(|| {
format!(
"{ty} {name} = {{ {values} }};",
ty = arg.to_c_type(),
"const {ty} {name}_vals[] = {{ {values} }};",
ty = arg.ty.c_scalar_type(),
name = arg.name,
values = arg.ty.populate_random(pass, &Language::C)
values = arg.ty.populate_random(loads, &Language::C)
)
})
})
.collect::<Vec<_>>()
.join("\n ")
.join("\n")
}

/// Creates a line that initializes this argument for Rust code.
/// e.g. `let a = transmute([0x1, 0x2]);`
pub fn init_random_values_rust(&self, pass: usize) -> String {
/// 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 {
self.iter()
.filter_map(|arg| {
(!arg.has_constraint()).then(|| {
if arg.is_simd() {
format!(
"let {name} = ::std::mem::transmute([{values}]);",
name = arg.name,
values = arg.ty.populate_random(pass, &Language::Rust),
)
} else {
format!(
"let {name} = {value};",
name = arg.name,
value = arg.ty.populate_random(pass, &Language::Rust)
)
}
format!(
"const {upper_name}_VALS: [{ty}; {load_size}] = unsafe{{ [{values}] }};",
upper_name = arg.name.to_uppercase(),
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)
)
})
})
.collect::<Vec<_>>()
.join("\n")
}

/// Creates a line for each argument that initalizes 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 {
self.iter()
.filter_map(|arg| {
// The ACLE doesn't support 64-bit polynomial loads on Armv7
// This and the cast are a workaround for this
let armv7_p64 = if let TypeKind::Poly = arg.ty.kind() {
p64_armv7_workaround
} else {
false
};

(!arg.has_constraint()).then(|| {
format!(
"{ty} {name} = {open_cast}{load}(&{name}_vals[i]){close_cast};",
ty = arg.to_c_type(),
name = arg.name,
load = if arg.is_simd() {
arg.ty.get_load_function(p64_armv7_workaround)
} else {
"*".to_string()
},
open_cast = if armv7_p64 {
format!("cast<{}>(", arg.to_c_type())
} else {
"".to_string()
},
close_cast = if armv7_p64 {
")".to_string()
} else {
"".to_string()
}
)
})
})
.collect::<Vec<_>>()
.join("\n ")
}

/// Creates a line for each argument that initalizes 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 {
self.iter()
.filter_map(|arg| {
(!arg.has_constraint()).then(|| {
format!(
"let {name} = {load}({upper_name}_VALS.as_ptr().offset(i));",
name = arg.name,
upper_name = arg.name.to_uppercase(),
load = if arg.is_simd() {
arg.ty.get_load_function(false)
} else {
"*".to_string()
},
)
})
})
.collect::<Vec<_>>()
.join("\n ")
}

pub fn iter(&self) -> std::slice::Iter<'_, Argument> {
self.args.iter()
}
Expand Down
65 changes: 37 additions & 28 deletions crates/intrinsic-test/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ pub struct Intrinsic {

impl Intrinsic {
/// Generates a std::cout for the intrinsics results that will match the
/// rust debug output format for the return type.
pub fn print_result_c(&self, index: usize, additional: &str) -> String {
/// 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 {
let lanes = if self.results.num_vectors() > 1 {
(0..self.results.num_vectors())
.map(|vector| {
Expand Down Expand Up @@ -72,7 +73,7 @@ impl Intrinsic {
};

format!(
r#"std::cout << "Result {additional}-{idx}: {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
r#"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 {
Expand All @@ -81,45 +82,53 @@ impl Intrinsic {
close = if self.results.is_simd() { ")" } else { "" },
lanes = lanes,
additional = additional,
idx = index,
)
}

pub fn generate_pass_rust(&self, index: usize, additional: &str) -> String {
pub fn generate_loop_c(
&self,
additional: &str,
passes: u32,
p64_armv7_workaround: bool,
) -> String {
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),
intrinsic_call = self.name,
args = self.arguments.as_call_param_c(),
print_result = self.print_result_c(additional)
)
}

pub fn generate_loop_rust(&self, additional: &str, passes: u32) -> String {
let constraints = self.arguments.as_constraint_parameters_rust();
let constraints = if !constraints.is_empty() {
format!("::<{}>", constraints)
} else {
constraints
};

format!(
r#"
unsafe {{
{initialized_args}
let res = {intrinsic_call}{const}({args});
println!("Result {additional}-{idx}: {{:.150?}}", res);
}}"#,
initialized_args = self.arguments.init_random_values_rust(index),
intrinsic_call = self.name,
args = self.arguments.as_call_param_rust(),
additional = additional,
idx = index,
const = constraints,
)
}

pub fn generate_pass_c(&self, index: usize, additional: &str) -> String {
format!(
r#" {{
{initialized_args}
auto __return_value = {intrinsic_call}({args});
{print_result}
for i in 0..{passes} {{
unsafe {{
{loaded_args}
let __return_value = {intrinsic_call}{const}({args});
println!("Result {additional}-{{}}: {{:.150?}}", i+1, __return_value);
}}
}}
}}"#,
initialized_args = self.arguments.init_random_values_c(index),
loaded_args = self.arguments.load_values_rust(),
intrinsic_call = self.name,
args = self.arguments.as_call_param_c(),
print_result = self.print_result_c(index, additional)
const = constraints,
args = self.arguments.as_call_param_rust(),
additional = additional,
)
}
}
Loading