1 use std::fmt;
2 use std::str::FromStr;
3 
4 use crate::values::values_for_pass;
5 use crate::Language;
6 
7 #[derive(Debug, PartialEq, Copy, Clone)]
8 pub enum TypeKind {
9     BFloat,
10     Float,
11     Int,
12     UInt,
13     Poly,
14     Void,
15 }
16 
17 impl FromStr for TypeKind {
18     type Err = String;
19 
from_str(s: &str) -> Result<Self, Self::Err>20     fn from_str(s: &str) -> Result<Self, Self::Err> {
21         match s {
22             "bfloat" => Ok(Self::BFloat),
23             "float" => Ok(Self::Float),
24             "int" => Ok(Self::Int),
25             "poly" => Ok(Self::Poly),
26             "uint" | "unsigned" => Ok(Self::UInt),
27             "void" => Ok(Self::Void),
28             _ => Err(format!("Impossible to parse argument kind {}", s)),
29         }
30     }
31 }
32 
33 impl fmt::Display for TypeKind {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result34     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35         write!(
36             f,
37             "{}",
38             match self {
39                 Self::BFloat => "bfloat",
40                 Self::Float => "float",
41                 Self::Int => "int",
42                 Self::UInt => "uint",
43                 Self::Poly => "poly",
44                 Self::Void => "void",
45             }
46         )
47     }
48 }
49 
50 impl TypeKind {
51     /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t.
c_prefix(&self) -> &str52     pub fn c_prefix(&self) -> &str {
53         match self {
54             Self::Float => "float",
55             Self::Int => "int",
56             Self::UInt => "uint",
57             Self::Poly => "poly",
58             _ => unreachable!("Not used: {:#?}", self),
59         }
60     }
61 
62     /// Gets the rust prefix for the type kind i.e. i, u, f.
rust_prefix(&self) -> &str63     pub fn rust_prefix(&self) -> &str {
64         match self {
65             Self::Float => "f",
66             Self::Int => "i",
67             Self::UInt => "u",
68             Self::Poly => "u",
69             _ => unreachable!("Unused type kind: {:#?}", self),
70         }
71     }
72 }
73 
74 #[derive(Debug, PartialEq, Clone)]
75 pub enum IntrinsicType {
76     Ptr {
77         constant: bool,
78         child: Box<IntrinsicType>,
79     },
80     Type {
81         constant: bool,
82         kind: TypeKind,
83         /// The bit length of this type (e.g. 32 for u32).
84         bit_len: Option<u32>,
85 
86         /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None`
87         /// means this is not a simd type. A `None` can be assumed to be 1,
88         /// although in some places a distinction is needed between `u64` and
89         /// `uint64x1_t` this signals that.
90         simd_len: Option<u32>,
91 
92         /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t).
93         /// A value of `None` represents a type that does not contain any
94         /// rows encoded in the type (e.g. uint8x8_t).
95         /// A value of `None` can be assumed to be 1 though.
96         vec_len: Option<u32>,
97     },
98 }
99 
100 impl IntrinsicType {
101     /// Get the TypeKind for this type, recursing into pointers.
kind(&self) -> TypeKind102     pub fn kind(&self) -> TypeKind {
103         match *self {
104             IntrinsicType::Ptr { ref child, .. } => child.kind(),
105             IntrinsicType::Type { kind, .. } => kind,
106         }
107     }
108 
109     /// Get the size of a single element inside this type, recursing into
110     /// pointers, i.e. a pointer to a u16 would be 16 rather than the size
111     /// of a pointer.
inner_size(&self) -> u32112     pub fn inner_size(&self) -> u32 {
113         match *self {
114             IntrinsicType::Ptr { ref child, .. } => child.inner_size(),
115             IntrinsicType::Type {
116                 bit_len: Some(bl), ..
117             } => bl,
118             _ => unreachable!(""),
119         }
120     }
121 
num_lanes(&self) -> u32122     pub fn num_lanes(&self) -> u32 {
123         match *self {
124             IntrinsicType::Ptr { ref child, .. } => child.num_lanes(),
125             IntrinsicType::Type {
126                 simd_len: Some(sl), ..
127             } => sl,
128             _ => 1,
129         }
130     }
131 
num_vectors(&self) -> u32132     pub fn num_vectors(&self) -> u32 {
133         match *self {
134             IntrinsicType::Ptr { ref child, .. } => child.num_vectors(),
135             IntrinsicType::Type {
136                 vec_len: Some(vl), ..
137             } => vl,
138             _ => 1,
139         }
140     }
141 
142     /// Determine if the type is a simd type, this will treat a type such as
143     /// `uint64x1` as simd.
is_simd(&self) -> bool144     pub fn is_simd(&self) -> bool {
145         match *self {
146             IntrinsicType::Ptr { ref child, .. } => child.is_simd(),
147             IntrinsicType::Type {
148                 simd_len: None,
149                 vec_len: None,
150                 ..
151             } => false,
152             _ => true,
153         }
154     }
155 
is_ptr(&self) -> bool156     pub fn is_ptr(&self) -> bool {
157         match *self {
158             IntrinsicType::Ptr { .. } => true,
159             IntrinsicType::Type { .. } => false,
160         }
161     }
162 
163     #[allow(unused)]
c_scalar_type(&self) -> String164     fn c_scalar_type(&self) -> String {
165         format!(
166             "{prefix}{bits}_t",
167             prefix = self.kind().c_prefix(),
168             bits = self.inner_size()
169         )
170     }
171 
rust_scalar_type(&self) -> String172     fn rust_scalar_type(&self) -> String {
173         format!(
174             "{prefix}{bits}",
175             prefix = self.kind().rust_prefix(),
176             bits = self.inner_size()
177         )
178     }
179 
180     /// Gets a string containing the typename for this type in C format.
c_type(&self) -> String181     pub fn c_type(&self) -> String {
182         match self {
183             IntrinsicType::Ptr { child, .. } => child.c_type(),
184             IntrinsicType::Type {
185                 constant,
186                 kind,
187                 bit_len: Some(bit_len),
188                 simd_len: None,
189                 vec_len: None,
190                 ..
191             } => format!(
192                 "{}{}{}_t",
193                 if *constant { "const " } else { "" },
194                 kind.c_prefix(),
195                 bit_len
196             ),
197             IntrinsicType::Type {
198                 kind,
199                 bit_len: Some(bit_len),
200                 simd_len: Some(simd_len),
201                 vec_len: None,
202                 ..
203             } => format!("{}{}x{}_t", kind.c_prefix(), bit_len, simd_len),
204             IntrinsicType::Type {
205                 kind,
206                 bit_len: Some(bit_len),
207                 simd_len: Some(simd_len),
208                 vec_len: Some(vec_len),
209                 ..
210             } => format!("{}{}x{}x{}_t", kind.c_prefix(), bit_len, simd_len, vec_len),
211             _ => todo!("{:#?}", self),
212         }
213     }
214 
c_single_vector_type(&self) -> String215     pub fn c_single_vector_type(&self) -> String {
216         match self {
217             IntrinsicType::Ptr { child, .. } => child.c_single_vector_type(),
218             IntrinsicType::Type {
219                 kind,
220                 bit_len: Some(bit_len),
221                 simd_len: Some(simd_len),
222                 vec_len: Some(_),
223                 ..
224             } => format!("{}{}x{}_t", kind.c_prefix(), bit_len, simd_len),
225             _ => unreachable!("Shouldn't be called on this type"),
226         }
227     }
228 
rust_type(&self) -> String229     pub fn rust_type(&self) -> String {
230         match self {
231             IntrinsicType::Ptr { child, .. } => child.c_type(),
232             IntrinsicType::Type {
233                 kind,
234                 bit_len: Some(bit_len),
235                 simd_len: None,
236                 vec_len: None,
237                 ..
238             } => format!("{}{}", kind.rust_prefix(), bit_len),
239             IntrinsicType::Type {
240                 kind,
241                 bit_len: Some(bit_len),
242                 simd_len: Some(simd_len),
243                 vec_len: None,
244                 ..
245             } => format!("{}{}x{}_t", kind.c_prefix(), bit_len, simd_len),
246             IntrinsicType::Type {
247                 kind,
248                 bit_len: Some(bit_len),
249                 simd_len: Some(simd_len),
250                 vec_len: Some(vec_len),
251                 ..
252             } => format!("{}{}x{}x{}_t", kind.c_prefix(), bit_len, simd_len, vec_len),
253             _ => todo!("{:#?}", self),
254         }
255     }
256 
257     /// Gets a cast for this type if needs promotion.
258     /// This is required for 8 bit types due to printing as the 8 bit types use
259     /// a char and when using that in `std::cout` it will print as a character,
260     /// which means value of 0 will be printed as a null byte.
c_promotion(&self) -> &str261     pub fn c_promotion(&self) -> &str {
262         match *self {
263             IntrinsicType::Type {
264                 kind,
265                 bit_len: Some(bit_len),
266                 ..
267             } if bit_len == 8 => match kind {
268                 TypeKind::Int => "(int)",
269                 TypeKind::UInt => "(unsigned int)",
270                 TypeKind::Poly => "(unsigned int)",
271                 _ => "",
272             },
273             _ => "",
274         }
275     }
276 
277     /// Generates a comma list of values that can be used to initialize an
278     /// argument for the intrinsic call.
279     /// This is determistic based on the pass number.
280     ///
281     /// * `pass`: The pass index, i.e. the iteration index for the call to an intrinsic
282     ///
283     /// Returns a string such as
284     /// * `0x1, 0x7F, 0xFF` if `language` is `Language::C`
285     /// * `0x1 as _, 0x7F as _, 0xFF as _` if `language` is `Language::Rust`
populate_random(&self, pass: usize, language: &Language) -> String286     pub fn populate_random(&self, pass: usize, language: &Language) -> String {
287         match self {
288             IntrinsicType::Ptr { child, .. } => child.populate_random(pass, language),
289             IntrinsicType::Type {
290                 bit_len: Some(bit_len),
291                 kind,
292                 simd_len,
293                 vec_len,
294                 ..
295             } if kind == &TypeKind::Int || kind == &TypeKind::UInt || kind == &TypeKind::Poly => (0
296                 ..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1)))
297                 .map(|i| {
298                     format!(
299                         "{}{}",
300                         values_for_pass(*bit_len, i, pass),
301                         match language {
302                             &Language::Rust => format!(" as {ty} ", ty = self.rust_scalar_type()),
303                             &Language::C => String::from(""),
304                         }
305                     )
306                 })
307                 .collect::<Vec<_>>()
308                 .join(","),
309             IntrinsicType::Type {
310                 kind: TypeKind::Float,
311                 bit_len: Some(32),
312                 simd_len,
313                 vec_len,
314                 ..
315             } => (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1)))
316                 .map(|i| {
317                     format!(
318                         "{}({})",
319                         match language {
320                             &Language::Rust => "f32::from_bits",
321                             &Language::C => "cast<float, uint32_t>",
322                         },
323                         values_for_pass(32, i, pass),
324                     )
325                 })
326                 .collect::<Vec<_>>()
327                 .join(","),
328             IntrinsicType::Type {
329                 kind: TypeKind::Float,
330                 bit_len: Some(64),
331                 simd_len,
332                 vec_len,
333                 ..
334             } => (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1)))
335                 .map(|i| {
336                     format!(
337                         "{}({}{})",
338                         match language {
339                             &Language::Rust => "f64::from_bits",
340                             &Language::C => "cast<double, uint64_t>",
341                         },
342                         values_for_pass(64, i, pass),
343                         match language {
344                             &Language::Rust => " as u64",
345                             &Language::C => "",
346                         }
347                     )
348                 })
349                 .collect::<Vec<_>>()
350                 .join(","),
351             _ => unreachable!("populate random: {:#?}", self),
352         }
353     }
354 
355     /// Determines the load function for this type.
356     #[allow(unused)]
get_load_function(&self) -> String357     pub fn get_load_function(&self) -> String {
358         match self {
359             IntrinsicType::Ptr { child, .. } => child.get_load_function(),
360             IntrinsicType::Type {
361                 kind: k,
362                 bit_len: Some(bl),
363                 simd_len,
364                 vec_len,
365                 ..
366             } => {
367                 let quad = if (simd_len.unwrap_or(1) * bl) > 64 {
368                     "q"
369                 } else {
370                     ""
371                 };
372                 format!(
373                     "vld{len}{quad}_{type}{size}",
374                     type = match k {
375                         TypeKind::UInt => "u",
376                         TypeKind::Int => "s",
377                         TypeKind::Float => "f",
378                         TypeKind::Poly => "p",
379                         x => todo!("get_load_function TypeKind: {:#?}", x),
380                     },
381                     size = bl,
382                     quad = quad,
383                     len = vec_len.unwrap_or(1),
384                 )
385             }
386             _ => todo!("get_load_function IntrinsicType: {:#?}", self),
387         }
388     }
389 
390     /// Determines the get lane function for this type.
get_lane_function(&self) -> String391     pub fn get_lane_function(&self) -> String {
392         match self {
393             IntrinsicType::Ptr { child, .. } => child.get_lane_function(),
394             IntrinsicType::Type {
395                 kind: k,
396                 bit_len: Some(bl),
397                 simd_len,
398                 ..
399             } => {
400                 let quad = if (simd_len.unwrap_or(1) * bl) > 64 {
401                     "q"
402                 } else {
403                     ""
404                 };
405                 format!(
406                     "vget{quad}_lane_{type}{size}",
407                     type = match k {
408                         TypeKind::UInt => "u",
409                         TypeKind::Int => "s",
410                         TypeKind::Float => "f",
411                         TypeKind::Poly => "p",
412                         x => todo!("get_load_function TypeKind: {:#?}", x),
413                     },
414                     size = bl,
415                     quad = quad,
416                 )
417             }
418             _ => todo!("get_lane_function IntrinsicType: {:#?}", self),
419         }
420     }
421 }
422