Skip to content

Commit 848f2e6

Browse files
committed
generate arrays of type-erased function pointers
i.e. don't duplicate blocks with loop logic. This compiles quite a bit faster, I think mostly because it's just less code
1 parent e62bcd9 commit 848f2e6

File tree

4 files changed

+69
-83
lines changed

4 files changed

+69
-83
lines changed

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,6 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType {
3333
}
3434
}
3535

36-
fn rust_type(&self) -> String {
37-
let rust_prefix = self.0.kind.rust_prefix();
38-
let c_prefix = self.0.kind.c_prefix();
39-
if self.0.ptr_constant {
40-
self.c_type()
41-
} else if let (Some(bit_len), simd_len, vec_len) =
42-
(self.0.bit_len, self.0.simd_len, self.0.vec_len)
43-
{
44-
match (simd_len, vec_len) {
45-
(None, None) => format!("{rust_prefix}{bit_len}"),
46-
(Some(simd), None) => format!("{c_prefix}{bit_len}x{simd}_t"),
47-
(Some(simd), Some(vec)) => format!("{c_prefix}{bit_len}x{simd}x{vec}_t"),
48-
(None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
49-
}
50-
} else {
51-
todo!("{:#?}", self)
52-
}
53-
}
54-
5536
/// Determines the load function for this type.
5637
fn get_load_function(&self, language: Language) -> String {
5738
if let IntrinsicType {

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,6 @@ where
114114
.join(", ")
115115
}
116116

117-
pub fn as_constraint_parameters_rust(&self) -> String {
118-
self.iter()
119-
.filter(|a| a.has_constraint())
120-
.map(|arg| arg.name.clone())
121-
.collect::<Vec<String>>()
122-
.join(", ")
123-
}
124-
125117
/// Creates a line for each argument that initializes an array for C from which `loads` argument
126118
/// values can be loaded as a sliding window.
127119
/// e.g `const int32x2_t a_vals = {0x3effffff, 0x3effffff, 0x3f7fffff}`, if loads=2.

crates/intrinsic-test/src/common/gen_rust.rs

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use itertools::Itertools;
22
use std::process::Command;
33

4-
use super::argument::Argument;
4+
use crate::common::constraint::Constraint;
5+
56
use super::indentation::Indentation;
67
use super::intrinsic::{IntrinsicDefinition, format_f16_return_value};
78
use super::intrinsic_helpers::IntrinsicTypeDefinition;
@@ -153,66 +154,84 @@ pub fn generate_rust_test_loop<T: IntrinsicTypeDefinition>(
153154
w: &mut impl std::io::Write,
154155
intrinsic: &dyn IntrinsicDefinition<T>,
155156
indentation: Indentation,
156-
additional: &str,
157+
specializations: &[Vec<u8>],
157158
passes: u32,
158159
) -> std::io::Result<()> {
159-
let constraints = intrinsic.arguments().as_constraint_parameters_rust();
160-
let constraints = if !constraints.is_empty() {
161-
format!("::<{constraints}>")
162-
} else {
163-
constraints
164-
};
160+
let intrinsic_name = intrinsic.name();
161+
162+
// Each function (and each specialization) has its own type. Erase that type with a cast.
163+
let mut coerce = String::from("unsafe fn(");
164+
for _ in intrinsic.arguments().iter().filter(|a| !a.has_constraint()) {
165+
coerce += "_, ";
166+
}
167+
coerce += ") -> _";
168+
169+
match specializations {
170+
[] => {
171+
writeln!(w, " let specializations = [(\"\", {intrinsic_name})];")?;
172+
}
173+
[const_args] if const_args.is_empty() => {
174+
writeln!(w, " let specializations = [(\"\", {intrinsic_name})];")?;
175+
}
176+
_ => {
177+
writeln!(w, " let specializations = [")?;
178+
179+
for specialization in specializations {
180+
let specialization: Vec<_> = specialization.iter().map(|d| d.to_string()).collect();
181+
182+
let const_args = specialization.join(",");
183+
let id = specialization.join("-");
184+
185+
writeln!(
186+
w,
187+
" (\"{id}\", {intrinsic_name}::<{const_args}> as {coerce}),"
188+
)?;
189+
}
190+
191+
writeln!(w, " ];")?;
192+
}
193+
}
165194

166195
let return_value = format_f16_return_value(intrinsic);
167196
let indentation2 = indentation.nested();
168197
let indentation3 = indentation2.nested();
169198
writeln!(
170199
w,
171-
"{indentation}for i in 0..{passes} {{\n\
172-
{indentation2}unsafe {{\n\
173-
{loaded_args}\
174-
{indentation3}let __return_value = {intrinsic_call}{const}({args});\n\
175-
{indentation3}println!(\"Result {additional}-{{}}: {{:?}}\", i + 1, {return_value});\n\
176-
{indentation2}}}\n\
177-
{indentation}}}",
200+
"\
201+
for (id, f) in specializations {{\n\
202+
for i in 0..{passes} {{\n\
203+
unsafe {{\n\
204+
{loaded_args}\
205+
let __return_value = f({args});\n\
206+
println!(\"Result {{id}}-{{}}: {{:?}}\", i + 1, {return_value});\n\
207+
}}\n\
208+
}}\n\
209+
}}",
178210
loaded_args = intrinsic.arguments().load_values_rust(indentation3),
179-
intrinsic_call = intrinsic.name(),
180-
const = constraints,
181211
args = intrinsic.arguments().as_call_param_rust(),
182212
)
183213
}
184214

185-
fn generate_rust_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>(
186-
w: &mut impl std::io::Write,
187-
intrinsic: &dyn IntrinsicDefinition<T>,
188-
indentation: Indentation,
189-
constraints: &mut (impl Iterator<Item = &'a Argument<T>> + Clone),
190-
name: String,
191-
) -> std::io::Result<()> {
192-
let Some(current) = constraints.next() else {
193-
return generate_rust_test_loop(w, intrinsic, indentation, &name, PASSES);
194-
};
195-
196-
let body_indentation = indentation.nested();
197-
for i in current.constraint.iter().flat_map(|c| c.to_range()) {
198-
let ty = current.ty.rust_type();
199-
200-
writeln!(w, "{indentation}{{")?;
201-
202-
writeln!(w, "{body_indentation}const {}: {ty} = {i};", current.name)?;
203-
204-
generate_rust_constraint_blocks(
205-
w,
206-
intrinsic,
207-
body_indentation,
208-
&mut constraints.clone(),
209-
format!("{name}-{i}"),
210-
)?;
211-
212-
writeln!(w, "{indentation}}}")?;
215+
/// Generate the specializations (unique sequences of const-generic arguments) for this intrinsic.
216+
fn generate_rust_specializations<'a>(
217+
constraints: &mut (impl Iterator<Item = &'a Constraint> + Clone),
218+
) -> Vec<Vec<u8>> {
219+
let mut specializations = vec![vec![]];
220+
221+
for constraint in constraints {
222+
specializations = specializations
223+
.into_iter()
224+
.flat_map(|const_args| {
225+
constraint.to_range().map(move |i| {
226+
let mut const_args = const_args.clone();
227+
const_args.push(u8::try_from(i).unwrap());
228+
const_args
229+
})
230+
})
231+
.collect();
213232
}
214233

215-
Ok(())
234+
specializations
216235
}
217236

218237
// Top-level function to create complete test program
@@ -230,13 +249,10 @@ pub fn create_rust_test_module<T: IntrinsicTypeDefinition>(
230249
arguments.gen_arglists_rust(w, indentation.nested(), PASSES)?;
231250

232251
// Define any const generics as `const` items, then generate the actual test loop.
233-
generate_rust_constraint_blocks(
234-
w,
235-
intrinsic,
236-
indentation.nested(),
237-
&mut arguments.iter().rev().filter(|i| i.has_constraint()),
238-
Default::default(),
239-
)?;
252+
let specializations =
253+
generate_rust_specializations(&mut arguments.iter().filter_map(|i| i.constraint.as_ref()));
254+
255+
generate_rust_test_loop(w, intrinsic, indentation, &specializations, PASSES)?;
240256

241257
writeln!(w, "}}")?;
242258

crates/intrinsic-test/src/common/intrinsic_helpers.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,4 @@ pub trait IntrinsicTypeDefinition: Deref<Target = IntrinsicType> {
290290

291291
/// can be directly defined in `impl` blocks
292292
fn c_single_vector_type(&self) -> String;
293-
294-
/// can be defined in `impl` blocks
295-
fn rust_type(&self) -> String;
296293
}

0 commit comments

Comments
 (0)