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