Skip to content

Commit 9039b44

Browse files
authored
Clean up the codegen a bit (particularly x86) (#116)
This builds on top of #115. There are no functional changes to the generated code (besides what #115 does), but cleans up the `fearless_simd_gen` code: - The `Arch` trait has been removed. It operated at the wrong level of abstraction--it makes no sense to call e.g. `mk_avx2::make_method` with any `Arch` implementation other than `X86`. - Many code generation functions in the AVX2 and SSE4.2 modules used to pass in the vector type along with its scalar and total bit widths. The former provides the latter, so we can stop passing all three in and just pass in the vector type.
1 parent 2425ecd commit 9039b44

File tree

14 files changed

+340
-457
lines changed

14 files changed

+340
-457
lines changed

fearless_simd_gen/src/arch/fallback.rs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33

44
#![expect(
55
clippy::match_single_binding,
6-
unreachable_pub,
76
reason = "TODO: https://github.com/linebender/fearless_simd/issues/40"
87
)]
98

10-
use crate::arch::Arch;
119
use crate::types::{ScalarType, VecType};
1210
use proc_macro2::{Ident, Span, TokenStream};
1311
use quote::quote;
@@ -64,35 +62,31 @@ pub(crate) fn translate_op(op: &str, is_float: bool) -> Option<&'static str> {
6462
})
6563
}
6664

67-
pub fn simple_intrinsic(name: &str, ty: &VecType) -> TokenStream {
68-
let ty_prefix = Fallback.arch_ty(ty);
65+
pub(crate) fn simple_intrinsic(name: &str, ty: &VecType) -> TokenStream {
66+
let ty_prefix = arch_ty(ty);
6967
let ident = Ident::new(name, Span::call_site());
7068

7169
quote! {#ty_prefix::#ident}
7270
}
7371

74-
pub struct Fallback;
75-
76-
impl Arch for Fallback {
77-
fn arch_ty(&self, ty: &VecType) -> TokenStream {
78-
let scalar = match ty.scalar {
79-
ScalarType::Float => "f",
80-
ScalarType::Unsigned => "u",
81-
ScalarType::Int | ScalarType::Mask => "i",
82-
};
83-
let name = format!("{}{}", scalar, ty.scalar_bits);
84-
let ident = Ident::new(&name, Span::call_site());
85-
quote! { #ident }
86-
}
72+
pub(crate) fn arch_ty(ty: &VecType) -> TokenStream {
73+
let scalar = match ty.scalar {
74+
ScalarType::Float => "f",
75+
ScalarType::Unsigned => "u",
76+
ScalarType::Int | ScalarType::Mask => "i",
77+
};
78+
let name = format!("{}{}", scalar, ty.scalar_bits);
79+
let ident = Ident::new(&name, Span::call_site());
80+
quote! { #ident }
81+
}
8782

88-
fn expr(&self, op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
89-
if let Some(translated) = translate_op(op, ty.scalar == ScalarType::Float) {
90-
let intrinsic = simple_intrinsic(translated, ty);
91-
quote! { #intrinsic ( #( #args ),* ) }
92-
} else {
93-
match op {
94-
_ => unimplemented!("missing {op}"),
95-
}
83+
pub(crate) fn expr(op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
84+
if let Some(translated) = translate_op(op, ty.scalar == ScalarType::Float) {
85+
let intrinsic = simple_intrinsic(translated, ty);
86+
quote! { #intrinsic ( #( #args ),* ) }
87+
} else {
88+
match op {
89+
_ => unimplemented!("missing {op}"),
9690
}
9791
}
9892
}

fearless_simd_gen/src/arch/mod.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,3 @@ pub(crate) mod fallback;
55
pub(crate) mod neon;
66
pub(crate) mod wasm;
77
pub(crate) mod x86;
8-
9-
use proc_macro2::TokenStream;
10-
11-
use crate::types::VecType;
12-
13-
pub(crate) trait Arch {
14-
fn arch_ty(&self, ty: &VecType) -> TokenStream;
15-
fn expr(&self, op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream;
16-
}

fearless_simd_gen/src/arch/neon.rs

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
// Copyright 2025 the Fearless_SIMD Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
#![expect(
5-
unreachable_pub,
6-
reason = "TODO: https://github.com/linebender/fearless_simd/issues/40"
7-
)]
8-
9-
use crate::arch::Arch;
104
use crate::types::{ScalarType, VecType};
115
use proc_macro2::{Ident, Span, TokenStream};
126
use quote::quote;
137

14-
pub struct Neon;
15-
168
fn translate_op(op: &str) -> Option<&'static str> {
179
Some(match op {
1810
"abs" => "vabs",
@@ -46,54 +38,52 @@ fn translate_op(op: &str) -> Option<&'static str> {
4638
})
4739
}
4840

49-
impl Arch for Neon {
50-
fn arch_ty(&self, ty: &VecType) -> TokenStream {
51-
let scalar = match ty.scalar {
52-
ScalarType::Float => "float",
53-
ScalarType::Unsigned => "uint",
54-
ScalarType::Int | ScalarType::Mask => "int",
55-
};
56-
let name = if ty.n_bits() == 256 {
57-
format!("{}{}x{}x2_t", scalar, ty.scalar_bits, ty.len / 2)
58-
} else if ty.n_bits() == 512 {
59-
format!("{}{}x{}x4_t", scalar, ty.scalar_bits, ty.len / 4)
60-
} else {
61-
format!("{}{}x{}_t", scalar, ty.scalar_bits, ty.len)
62-
};
63-
let ident = Ident::new(&name, Span::call_site());
64-
quote! { #ident }
65-
}
41+
pub(crate) fn arch_ty(ty: &VecType) -> TokenStream {
42+
let scalar = match ty.scalar {
43+
ScalarType::Float => "float",
44+
ScalarType::Unsigned => "uint",
45+
ScalarType::Int | ScalarType::Mask => "int",
46+
};
47+
let name = if ty.n_bits() == 256 {
48+
format!("{}{}x{}x2_t", scalar, ty.scalar_bits, ty.len / 2)
49+
} else if ty.n_bits() == 512 {
50+
format!("{}{}x{}x4_t", scalar, ty.scalar_bits, ty.len / 4)
51+
} else {
52+
format!("{}{}x{}_t", scalar, ty.scalar_bits, ty.len)
53+
};
54+
let ident = Ident::new(&name, Span::call_site());
55+
quote! { #ident }
56+
}
6657

67-
// expects args and return value in arch dialect
68-
fn expr(&self, op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
69-
// There is no logical NOT for 64-bit, so we need this workaround.
70-
if op == "not" && ty.scalar_bits == 64 && ty.scalar == ScalarType::Mask {
71-
return quote! { vreinterpretq_s64_s32(vmvnq_s32(vreinterpretq_s32_s64(a.into()))) };
72-
}
58+
// expects args and return value in arch dialect
59+
pub(crate) fn expr(op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
60+
// There is no logical NOT for 64-bit, so we need this workaround.
61+
if op == "not" && ty.scalar_bits == 64 && ty.scalar == ScalarType::Mask {
62+
return quote! { vreinterpretq_s64_s32(vmvnq_s32(vreinterpretq_s32_s64(a.into()))) };
63+
}
7364

74-
if let Some(xlat) = translate_op(op) {
75-
let intrinsic = simple_intrinsic(xlat, ty);
76-
return quote! { #intrinsic ( #( #args ),* ) };
65+
if let Some(xlat) = translate_op(op) {
66+
let intrinsic = simple_intrinsic(xlat, ty);
67+
return quote! { #intrinsic ( #( #args ),* ) };
68+
}
69+
match op {
70+
"splat" => {
71+
let intrinsic = split_intrinsic("vdup", "n", ty);
72+
quote! { #intrinsic ( #( #args ),* ) }
7773
}
78-
match op {
79-
"splat" => {
80-
let intrinsic = split_intrinsic("vdup", "n", ty);
81-
quote! { #intrinsic ( #( #args ),* ) }
82-
}
83-
"fract" => {
84-
let to = VecType::new(ScalarType::Int, ty.scalar_bits, ty.len);
85-
let c1 = cvt_intrinsic("vcvt", &to, ty);
86-
let c2 = cvt_intrinsic("vcvt", ty, &to);
87-
let sub = simple_intrinsic("vsub", ty);
88-
quote! {
89-
let c1 = #c1(a.into());
90-
let c2 = #c2(c1);
74+
"fract" => {
75+
let to = VecType::new(ScalarType::Int, ty.scalar_bits, ty.len);
76+
let c1 = cvt_intrinsic("vcvt", &to, ty);
77+
let c2 = cvt_intrinsic("vcvt", ty, &to);
78+
let sub = simple_intrinsic("vsub", ty);
79+
quote! {
80+
let c1 = #c1(a.into());
81+
let c2 = #c2(c1);
9182

92-
#sub(a.into(), c2)
93-
}
83+
#sub(a.into(), c2)
9484
}
95-
_ => unimplemented!("missing {op}"),
9685
}
86+
_ => unimplemented!("missing {op}"),
9787
}
9888
}
9989

@@ -106,31 +96,31 @@ fn neon_array_type(ty: &VecType) -> (&'static str, &'static str, usize) {
10696
(opt_q(ty), scalar_c, ty.scalar_bits)
10797
}
10898

109-
pub fn opt_q(ty: &VecType) -> &'static str {
99+
pub(crate) fn opt_q(ty: &VecType) -> &'static str {
110100
match ty.n_bits() {
111101
64 => "",
112102
128 => "q",
113103
_ => panic!("unsupported simd width"),
114104
}
115105
}
116106

117-
pub fn simple_intrinsic(name: &str, ty: &VecType) -> Ident {
107+
pub(crate) fn simple_intrinsic(name: &str, ty: &VecType) -> Ident {
118108
let (opt_q, scalar_c, size) = neon_array_type(ty);
119109
Ident::new(
120110
&format!("{name}{opt_q}_{scalar_c}{size}"),
121111
Span::call_site(),
122112
)
123113
}
124114

125-
pub fn split_intrinsic(name: &str, name2: &str, ty: &VecType) -> Ident {
115+
pub(crate) fn split_intrinsic(name: &str, name2: &str, ty: &VecType) -> Ident {
126116
let (opt_q, scalar_c, size) = neon_array_type(ty);
127117
Ident::new(
128118
&format!("{name}{opt_q}_{name2}_{scalar_c}{size}"),
129119
Span::call_site(),
130120
)
131121
}
132122

133-
pub fn cvt_intrinsic(name: &str, to_ty: &VecType, from_ty: &VecType) -> Ident {
123+
pub(crate) fn cvt_intrinsic(name: &str, to_ty: &VecType, from_ty: &VecType) -> Ident {
134124
let (opt_q, from_scalar_c, from_size) = neon_array_type(from_ty);
135125
let (_opt_q, to_scalar_c, to_size) = neon_array_type(to_ty);
136126
Ident::new(

fearless_simd_gen/src/arch/wasm.rs

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
#![expect(
55
clippy::match_single_binding,
66
clippy::uninlined_format_args,
7-
unreachable_pub,
87
reason = "TODO: https://github.com/linebender/fearless_simd/issues/40"
98
)]
109

11-
use crate::arch::Arch;
1210
use crate::types::{ScalarType, VecType};
1311
use proc_macro2::{Ident, Span, TokenStream};
1412
use quote::quote;
1513

16-
pub struct Wasm;
17-
1814
fn translate_op(op: &str) -> Option<&'static str> {
1915
Some(match op {
2016
"abs" => "abs",
@@ -48,7 +44,7 @@ fn translate_op(op: &str) -> Option<&'static str> {
4844
}
4945

5046
fn simple_intrinsic(name: &str, ty: &VecType) -> TokenStream {
51-
let ty_prefix = Wasm.arch_ty(ty);
47+
let ty_prefix = arch_ty(ty);
5248
let ident = Ident::new(name, Span::call_site());
5349
let combined_ident = Ident::new(&format!("{}_{}", ty_prefix, ident), Span::call_site());
5450
quote! { #combined_ident }
@@ -61,35 +57,33 @@ fn v128_intrinsic(name: &str) -> TokenStream {
6157
quote! { #combined_ident }
6258
}
6359

64-
impl Arch for Wasm {
65-
fn arch_ty(&self, ty: &VecType) -> TokenStream {
66-
let scalar = match ty.scalar {
67-
ScalarType::Float => "f",
68-
ScalarType::Unsigned => "u",
69-
ScalarType::Int | ScalarType::Mask => "i",
70-
};
71-
let name = format!("{}{}x{}", scalar, ty.scalar_bits, ty.len);
72-
let ident = Ident::new(&name, Span::call_site());
73-
quote! { #ident }
74-
}
60+
pub(crate) fn arch_ty(ty: &VecType) -> TokenStream {
61+
let scalar = match ty.scalar {
62+
ScalarType::Float => "f",
63+
ScalarType::Unsigned => "u",
64+
ScalarType::Int | ScalarType::Mask => "i",
65+
};
66+
let name = format!("{}{}x{}", scalar, ty.scalar_bits, ty.len);
67+
let ident = Ident::new(&name, Span::call_site());
68+
quote! { #ident }
69+
}
7570

76-
// expects args and return value in arch dialect
77-
fn expr(&self, op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
78-
if let Some(translated) = translate_op(op) {
79-
let intrinsic = match translated {
80-
"not" => v128_intrinsic(translated),
81-
"and" => v128_intrinsic(translated),
82-
"or" => v128_intrinsic(translated),
83-
"xor" => v128_intrinsic(translated),
84-
_ => simple_intrinsic(translated, ty),
85-
};
71+
// expects args and return value in arch dialect
72+
pub(crate) fn expr(op: &str, ty: &VecType, args: &[TokenStream]) -> TokenStream {
73+
if let Some(translated) = translate_op(op) {
74+
let intrinsic = match translated {
75+
"not" => v128_intrinsic(translated),
76+
"and" => v128_intrinsic(translated),
77+
"or" => v128_intrinsic(translated),
78+
"xor" => v128_intrinsic(translated),
79+
_ => simple_intrinsic(translated, ty),
80+
};
8681

87-
quote! { #intrinsic ( #( #args ),* ) }
88-
} else {
89-
match op {
90-
// Add any special case operations here if needed
91-
_ => unimplemented!("missing {op}"),
92-
}
82+
quote! { #intrinsic ( #( #args ),* ) }
83+
} else {
84+
match op {
85+
// Add any special case operations here if needed
86+
_ => unimplemented!("missing {op}"),
9387
}
9488
}
9589
}

0 commit comments

Comments
 (0)