]>
Commit | Line | Data |
---|---|---|
0531ce1d | 1 | #![allow(bad_style)] |
416331ca | 2 | #![allow(unused)] |
0731742a XL |
3 | #![allow( |
4 | clippy::shadow_reuse, | |
5 | clippy::cast_lossless, | |
6 | clippy::match_same_arms, | |
7 | clippy::nonminimal_bool, | |
8 | clippy::print_stdout, | |
9 | clippy::use_debug, | |
10 | clippy::eq_op, | |
11 | clippy::useless_format | |
8faf50e0 | 12 | )] |
0531ce1d | 13 | |
0531ce1d XL |
14 | use std::collections::{BTreeMap, HashMap}; |
15 | ||
0731742a | 16 | use serde::Deserialize; |
0531ce1d XL |
17 | |
18 | const PRINT_INSTRUCTION_VIOLATIONS: bool = false; | |
19 | const PRINT_MISSING_LISTS: bool = false; | |
20 | const PRINT_MISSING_LISTS_MARKDOWN: bool = false; | |
21 | ||
22 | struct Function { | |
23 | name: &'static str, | |
24 | arguments: &'static [&'static Type], | |
25 | ret: Option<&'static Type>, | |
26 | target_feature: Option<&'static str>, | |
27 | instrs: &'static [&'static str], | |
28 | file: &'static str, | |
29 | required_const: &'static [usize], | |
e1599b0c | 30 | has_test: bool, |
0531ce1d XL |
31 | } |
32 | ||
33 | static F32: Type = Type::PrimFloat(32); | |
34 | static F64: Type = Type::PrimFloat(64); | |
35 | static I16: Type = Type::PrimSigned(16); | |
36 | static I32: Type = Type::PrimSigned(32); | |
37 | static I64: Type = Type::PrimSigned(64); | |
38 | static I8: Type = Type::PrimSigned(8); | |
39 | static U16: Type = Type::PrimUnsigned(16); | |
40 | static U32: Type = Type::PrimUnsigned(32); | |
41 | static U64: Type = Type::PrimUnsigned(64); | |
0731742a | 42 | static U128: Type = Type::PrimUnsigned(128); |
0531ce1d | 43 | static U8: Type = Type::PrimUnsigned(8); |
0731742a | 44 | static ORDERING: Type = Type::Ordering; |
0531ce1d XL |
45 | |
46 | static M64: Type = Type::M64; | |
47 | static M128: Type = Type::M128; | |
48 | static M128I: Type = Type::M128I; | |
49 | static M128D: Type = Type::M128D; | |
50 | static M256: Type = Type::M256; | |
51 | static M256I: Type = Type::M256I; | |
52 | static M256D: Type = Type::M256D; | |
0731742a XL |
53 | static M512: Type = Type::M512; |
54 | static M512I: Type = Type::M512I; | |
55 | static M512D: Type = Type::M512D; | |
f9f354fc | 56 | static MMASK8: Type = Type::MMASK8; |
0731742a | 57 | static MMASK16: Type = Type::MMASK16; |
3dfed10e | 58 | static MM_CMPINT_ENUM: Type = Type::MM_CMPINT_ENUM; |
1b1a35ee XL |
59 | static MM_MANTISSA_NORM_ENUM: Type = Type::MM_MANTISSA_NORM_ENUM; |
60 | static MM_MANTISSA_SIGN_ENUM: Type = Type::MM_MANTISSA_SIGN_ENUM; | |
61 | static MM_PERM_ENUM: Type = Type::MM_PERM_ENUM; | |
0531ce1d XL |
62 | |
63 | static TUPLE: Type = Type::Tuple; | |
64 | static CPUID: Type = Type::CpuidResult; | |
0731742a | 65 | static NEVER: Type = Type::Never; |
0531ce1d XL |
66 | |
67 | #[derive(Debug)] | |
68 | enum Type { | |
69 | PrimFloat(u8), | |
70 | PrimSigned(u8), | |
71 | PrimUnsigned(u8), | |
416331ca XL |
72 | MutPtr(&'static Type), |
73 | ConstPtr(&'static Type), | |
0531ce1d XL |
74 | M64, |
75 | M128, | |
76 | M128D, | |
77 | M128I, | |
78 | M256, | |
79 | M256D, | |
80 | M256I, | |
0731742a XL |
81 | M512, |
82 | M512D, | |
83 | M512I, | |
f9f354fc | 84 | MMASK8, |
0731742a | 85 | MMASK16, |
3dfed10e | 86 | MM_CMPINT_ENUM, |
1b1a35ee XL |
87 | MM_MANTISSA_NORM_ENUM, |
88 | MM_MANTISSA_SIGN_ENUM, | |
89 | MM_PERM_ENUM, | |
0531ce1d XL |
90 | Tuple, |
91 | CpuidResult, | |
0731742a XL |
92 | Never, |
93 | Ordering, | |
0531ce1d XL |
94 | } |
95 | ||
416331ca | 96 | stdarch_verify::x86_functions!(static FUNCTIONS); |
0531ce1d XL |
97 | |
98 | #[derive(Deserialize)] | |
99 | struct Data { | |
100 | #[serde(rename = "intrinsic", default)] | |
101 | intrinsics: Vec<Intrinsic>, | |
102 | } | |
103 | ||
104 | #[derive(Deserialize)] | |
105 | struct Intrinsic { | |
3dfed10e XL |
106 | #[serde(rename = "return")] |
107 | return_: Return, | |
0531ce1d XL |
108 | name: String, |
109 | #[serde(rename = "CPUID", default)] | |
110 | cpuid: Vec<String>, | |
111 | #[serde(rename = "parameter", default)] | |
112 | parameters: Vec<Parameter>, | |
113 | #[serde(default)] | |
114 | instruction: Vec<Instruction>, | |
115 | } | |
116 | ||
117 | #[derive(Deserialize)] | |
118 | struct Parameter { | |
119 | #[serde(rename = "type")] | |
120 | type_: String, | |
121 | } | |
122 | ||
3dfed10e XL |
123 | #[derive(Deserialize)] |
124 | struct Return { | |
125 | #[serde(rename = "type")] | |
126 | type_: String, | |
127 | } | |
128 | ||
0531ce1d XL |
129 | #[derive(Deserialize, Debug)] |
130 | struct Instruction { | |
131 | name: String, | |
132 | } | |
133 | ||
134 | macro_rules! bail { | |
135 | ($($t:tt)*) => (return Err(format!($($t)*))) | |
136 | } | |
137 | ||
138 | #[test] | |
139 | fn verify_all_signatures() { | |
140 | // This XML document was downloaded from Intel's site. To update this you | |
141 | // can visit intel's intrinsics guide online documentation: | |
142 | // | |
143 | // https://software.intel.com/sites/landingpage/IntrinsicsGuide/# | |
144 | // | |
145 | // Open up the network console and you'll see an xml file was downloaded | |
146 | // (currently called data-3.4.xml). That's the file we downloaded | |
147 | // here. | |
148 | let xml = include_bytes!("../x86-intel.xml"); | |
149 | ||
150 | let xml = &xml[..]; | |
9fa01778 | 151 | let data: Data = serde_xml_rs::from_reader(xml).expect("failed to deserialize xml"); |
0531ce1d XL |
152 | let mut map = HashMap::new(); |
153 | for intrinsic in &data.intrinsics { | |
154 | map.entry(&intrinsic.name[..]) | |
155 | .or_insert_with(Vec::new) | |
156 | .push(intrinsic); | |
157 | } | |
158 | ||
159 | let mut all_valid = true; | |
160 | 'outer: for rust in FUNCTIONS { | |
e1599b0c XL |
161 | if !rust.has_test { |
162 | // FIXME: this list should be almost empty | |
163 | let skip = [ | |
164 | "__readeflags", | |
165 | "__readeflags", | |
166 | "__writeeflags", | |
167 | "__writeeflags", | |
168 | "_mm_comige_ss", | |
169 | "_mm_cvt_ss2si", | |
170 | "_mm_cvtt_ss2si", | |
171 | "_mm_cvt_si2ss", | |
172 | "_mm_set_ps1", | |
173 | "_mm_load_ps1", | |
174 | "_mm_store_ps1", | |
175 | "_mm_getcsr", | |
176 | "_mm_setcsr", | |
177 | "_MM_GET_EXCEPTION_MASK", | |
178 | "_MM_GET_EXCEPTION_STATE", | |
179 | "_MM_GET_FLUSH_ZERO_MODE", | |
180 | "_MM_GET_ROUNDING_MODE", | |
181 | "_MM_SET_EXCEPTION_MASK", | |
182 | "_MM_SET_EXCEPTION_STATE", | |
183 | "_MM_SET_FLUSH_ZERO_MODE", | |
184 | "_MM_SET_ROUNDING_MODE", | |
185 | "_mm_prefetch", | |
186 | "_mm_undefined_ps", | |
187 | "_m_pmaxsw", | |
188 | "_m_pmaxub", | |
189 | "_m_pminsw", | |
190 | "_m_pminub", | |
191 | "_m_pavgb", | |
192 | "_m_pavgw", | |
193 | "_m_psadbw", | |
194 | "_mm_cvt_pi2ps", | |
195 | "_m_maskmovq", | |
196 | "_m_pextrw", | |
197 | "_m_pinsrw", | |
198 | "_m_pmovmskb", | |
199 | "_m_pshufw", | |
200 | "_mm_cvtt_ps2pi", | |
201 | "_mm_cvt_ps2pi", | |
202 | "__cpuid_count", | |
203 | "__cpuid", | |
204 | "__get_cpuid_max", | |
205 | "_xsave", | |
206 | "_xrstor", | |
207 | "_xsetbv", | |
208 | "_xgetbv", | |
209 | "_xsaveopt", | |
210 | "_xsavec", | |
211 | "_xsaves", | |
212 | "_xrstors", | |
213 | "_mm_bslli_si128", | |
214 | "_mm_bsrli_si128", | |
215 | "_mm_undefined_pd", | |
216 | "_mm_undefined_si128", | |
217 | "_mm_cvtps_ph", | |
218 | "_mm256_cvtps_ph", | |
219 | "_rdtsc", | |
220 | "__rdtscp", | |
221 | "_mm256_castps128_ps256", | |
222 | "_mm256_castpd128_pd256", | |
223 | "_mm256_castsi128_si256", | |
224 | "_mm256_undefined_ps", | |
225 | "_mm256_undefined_pd", | |
226 | "_mm256_undefined_si256", | |
227 | "_bextr2_u32", | |
228 | "_mm_tzcnt_32", | |
e1599b0c XL |
229 | "_m_paddb", |
230 | "_m_paddw", | |
231 | "_m_paddd", | |
232 | "_m_paddsb", | |
233 | "_m_paddsw", | |
234 | "_m_paddusb", | |
235 | "_m_paddusw", | |
236 | "_m_psubb", | |
237 | "_m_psubw", | |
238 | "_m_psubd", | |
239 | "_m_psubsb", | |
240 | "_m_psubsw", | |
241 | "_m_psubusb", | |
242 | "_m_psubusw", | |
243 | "_mm_set_pi16", | |
244 | "_mm_set_pi32", | |
245 | "_mm_set_pi8", | |
246 | "_mm_set1_pi16", | |
247 | "_mm_set1_pi32", | |
248 | "_mm_set1_pi8", | |
249 | "_mm_setr_pi16", | |
250 | "_mm_setr_pi32", | |
251 | "_mm_setr_pi8", | |
252 | "ud2", | |
253 | "_mm_min_epi8", | |
254 | "_mm_min_epi32", | |
255 | "_xbegin", | |
256 | "_xend", | |
257 | "_rdrand16_step", | |
258 | "_rdrand32_step", | |
259 | "_rdseed16_step", | |
260 | "_rdseed32_step", | |
261 | "_fxsave", | |
262 | "_fxrstor", | |
263 | "_t1mskc_u64", | |
264 | "_mm256_shuffle_epi32", | |
265 | "_mm256_bslli_epi128", | |
266 | "_mm256_bsrli_epi128", | |
267 | "_mm256_unpackhi_epi8", | |
268 | "_mm256_unpacklo_epi8", | |
269 | "_mm256_unpackhi_epi16", | |
270 | "_mm256_unpacklo_epi16", | |
271 | "_mm256_unpackhi_epi32", | |
272 | "_mm256_unpacklo_epi32", | |
273 | "_mm256_unpackhi_epi64", | |
274 | "_mm256_unpacklo_epi64", | |
275 | "_xsave64", | |
276 | "_xrstor64", | |
277 | "_xsaveopt64", | |
278 | "_xsavec64", | |
279 | "_xsaves64", | |
280 | "_xrstors64", | |
281 | "_mm_cvtsi64x_si128", | |
282 | "_mm_cvtsi128_si64x", | |
283 | "_mm_cvtsi64x_sd", | |
284 | "cmpxchg16b", | |
285 | "_rdrand64_step", | |
286 | "_rdseed64_step", | |
287 | "_bextr2_u64", | |
288 | "_mm_tzcnt_64", | |
289 | "_fxsave64", | |
290 | "_fxrstor64", | |
3dfed10e XL |
291 | "_mm512_undefined_ps", |
292 | "_mm512_undefined_pd", | |
e1599b0c XL |
293 | ]; |
294 | if !skip.contains(&rust.name) { | |
295 | println!( | |
296 | "missing run-time test named `test_{}` for `{}`", | |
297 | { | |
298 | let mut id = rust.name; | |
299 | while id.starts_with('_') { | |
300 | id = &id[1..]; | |
301 | } | |
302 | id | |
303 | }, | |
304 | rust.name | |
305 | ); | |
306 | all_valid = false; | |
307 | } | |
308 | } | |
309 | ||
0531ce1d | 310 | match rust.name { |
0731742a XL |
311 | // These aren't defined by Intel but they're defined by what appears |
312 | // to be all other compilers. For more information see | |
416331ca | 313 | // rust-lang/stdarch#307, and otherwise these signatures |
0731742a XL |
314 | // have all been manually verified. |
315 | "__readeflags" | | |
316 | "__writeeflags" | | |
317 | "__cpuid_count" | | |
318 | "__cpuid" | | |
319 | "__get_cpuid_max" | | |
320 | // Not listed with intel, but manually verified | |
321 | "cmpxchg16b" | | |
322 | // The UD2 intrinsic is not defined by Intel, but it was agreed on | |
323 | // in the RFC Issue 2512: | |
324 | // https://github.com/rust-lang/rfcs/issues/2512 | |
325 | "ud2" | |
326 | => continue, | |
48663c56 XL |
327 | // Intel requires the mask argument for _mm_shuffle_ps to be an |
328 | // unsigned integer, but all other _mm_shuffle_.. intrinsics | |
329 | // take a signed-integer. This breaks `_MM_SHUFFLE` for | |
330 | // `_mm_shuffle_ps`: | |
331 | "_mm_shuffle_ps" => continue, | |
0531ce1d XL |
332 | _ => {} |
333 | } | |
334 | ||
335 | // these are all AMD-specific intrinsics | |
336 | if let Some(feature) = rust.target_feature { | |
337 | if feature.contains("sse4a") || feature.contains("tbm") { | |
338 | continue; | |
339 | } | |
340 | } | |
341 | ||
342 | let intel = match map.remove(rust.name) { | |
343 | Some(i) => i, | |
344 | None => panic!("missing intel definition for {}", rust.name), | |
345 | }; | |
346 | ||
347 | let mut errors = Vec::new(); | |
348 | for intel in intel { | |
349 | match matches(rust, intel) { | |
350 | Ok(()) => continue 'outer, | |
351 | Err(e) => errors.push(e), | |
352 | } | |
353 | } | |
354 | println!("failed to verify `{}`", rust.name); | |
355 | for error in errors { | |
356 | println!(" * {}", error); | |
357 | } | |
358 | all_valid = false; | |
359 | } | |
360 | assert!(all_valid); | |
361 | ||
362 | let mut missing = BTreeMap::new(); | |
363 | for (name, intel) in &map { | |
364 | // currently focused mainly on missing SIMD intrinsics, but there's | |
365 | // definitely some other assorted ones that we're missing. | |
366 | if !name.starts_with("_mm") { | |
367 | continue; | |
368 | } | |
369 | ||
370 | // we'll get to avx-512 later | |
371 | // let avx512 = intel.iter().any(|i| { | |
372 | // i.name.starts_with("_mm512") || i.cpuid.iter().any(|c| { | |
373 | // c.contains("512") | |
374 | // }) | |
375 | // }); | |
376 | // if avx512 { | |
377 | // continue | |
378 | // } | |
379 | ||
380 | for intel in intel { | |
381 | missing | |
382 | .entry(&intel.cpuid) | |
383 | .or_insert_with(Vec::new) | |
384 | .push(intel); | |
385 | } | |
386 | } | |
387 | ||
388 | // generate a bulleted list of missing intrinsics | |
389 | if PRINT_MISSING_LISTS || PRINT_MISSING_LISTS_MARKDOWN { | |
390 | for (k, v) in missing { | |
391 | if PRINT_MISSING_LISTS_MARKDOWN { | |
392 | println!("\n<details><summary>{:?}</summary><p>\n", k); | |
393 | for intel in v { | |
394 | let url = format!( | |
395 | "https://software.intel.com/sites/landingpage\ | |
396 | /IntrinsicsGuide/#text={}&expand=5236", | |
397 | intel.name | |
398 | ); | |
399 | println!(" * [ ] [`{}`]({})", intel.name, url); | |
400 | } | |
401 | println!("</p></details>\n"); | |
402 | } else { | |
403 | println!("\n{:?}\n", k); | |
404 | for intel in v { | |
405 | println!("\t{}", intel.name); | |
406 | } | |
407 | } | |
408 | } | |
409 | } | |
410 | } | |
411 | ||
412 | fn matches(rust: &Function, intel: &Intrinsic) -> Result<(), String> { | |
413 | // Verify that all `#[target_feature]` annotations are correct, | |
414 | // ensuring that we've actually enabled the right instruction | |
415 | // set for this intrinsic. | |
416 | match rust.name { | |
0731742a XL |
417 | "_bswap" | "_bswap64" => {} |
418 | ||
419 | // These don't actually have a target feature unlike their brethren with | |
420 | // the `x` inside the name which requires adx | |
421 | "_addcarry_u32" | "_addcarry_u64" | "_subborrow_u32" | "_subborrow_u64" => {} | |
422 | ||
532ac7d7 XL |
423 | "_bittest" |
424 | | "_bittestandset" | |
425 | | "_bittestandreset" | |
426 | | "_bittestandcomplement" | |
427 | | "_bittest64" | |
428 | | "_bittestandset64" | |
429 | | "_bittestandreset64" | |
430 | | "_bittestandcomplement64" => {} | |
431 | ||
0531ce1d XL |
432 | _ => { |
433 | if intel.cpuid.is_empty() { | |
434 | bail!("missing cpuid for {}", rust.name); | |
435 | } | |
436 | } | |
437 | } | |
438 | ||
439 | for cpuid in &intel.cpuid { | |
532ac7d7 XL |
440 | // The pause intrinsic is in the SSE2 module, but it is backwards |
441 | // compatible with CPUs without SSE2, and it therefore does not need the | |
442 | // target-feature attribute. | |
443 | if rust.name == "_mm_pause" { | |
444 | continue; | |
445 | } | |
0531ce1d XL |
446 | // this is needed by _xsave and probably some related intrinsics, |
447 | // but let's just skip it for now. | |
448 | if *cpuid == "XSS" { | |
449 | continue; | |
450 | } | |
451 | ||
452 | // these flags on the rdtsc/rtdscp intrinsics we don't test for right | |
453 | // now, but we may wish to add these one day! | |
454 | // | |
455 | // For more info see #308 | |
456 | if *cpuid == "TSC" || *cpuid == "RDTSCP" { | |
457 | continue; | |
458 | } | |
459 | ||
460 | let cpuid = cpuid | |
461 | .chars() | |
462 | .flat_map(|c| c.to_lowercase()) | |
463 | .collect::<String>(); | |
464 | ||
416331ca | 465 | // Fix mismatching feature names: |
9fa01778 | 466 | let fixup_cpuid = |cpuid: String| match cpuid.as_ref() { |
416331ca XL |
467 | // The XML file names IFMA as "avx512ifma52", while Rust calls |
468 | // it "avx512ifma". | |
9fa01778 | 469 | "avx512ifma52" => String::from("avx512ifma"), |
3dfed10e XL |
470 | // Some AVX512f intrinsics are also supported by Knight's Corner. |
471 | // The XML lists them as avx512f/kncni, but we are solely gating | |
472 | // them behind avx512f since we don't have a KNC feature yet. | |
473 | "avx512f/kncni" => String::from("avx512f"), | |
416331ca XL |
474 | // See: https://github.com/rust-lang/stdarch/issues/738 |
475 | // The intrinsics guide calls `f16c` `fp16c` in disagreement with | |
476 | // Intel's architecture manuals. | |
477 | "fp16c" => String::from("f16c"), | |
9fa01778 XL |
478 | _ => cpuid, |
479 | }; | |
480 | let fixed_cpuid = fixup_cpuid(cpuid); | |
481 | ||
8faf50e0 XL |
482 | let rust_feature = rust |
483 | .target_feature | |
484 | .expect(&format!("no target feature listed for {}", rust.name)); | |
9fa01778 XL |
485 | |
486 | if rust_feature.contains(&fixed_cpuid) { | |
0531ce1d XL |
487 | continue; |
488 | } | |
489 | bail!( | |
490 | "intel cpuid `{}` not in `{}` for {}", | |
9fa01778 | 491 | fixed_cpuid, |
0531ce1d XL |
492 | rust_feature, |
493 | rust.name | |
494 | ) | |
495 | } | |
496 | ||
497 | if PRINT_INSTRUCTION_VIOLATIONS { | |
498 | if rust.instrs.is_empty() { | |
499 | if !intel.instruction.is_empty() { | |
500 | println!( | |
501 | "instruction not listed for `{}`, but intel lists {:?}", | |
502 | rust.name, intel.instruction | |
503 | ); | |
504 | } | |
505 | ||
506 | // If intel doesn't list any instructions and we do then don't | |
507 | // bother trying to look for instructions in intel, we've just got | |
508 | // some extra assertions on our end. | |
509 | } else if !intel.instruction.is_empty() { | |
510 | for instr in rust.instrs { | |
0731742a | 511 | let asserting = intel.instruction.iter().any(|a| a.name.starts_with(instr)); |
0531ce1d XL |
512 | if !asserting { |
513 | println!( | |
514 | "intel failed to list `{}` as an instruction for `{}`", | |
515 | instr, rust.name | |
516 | ); | |
517 | } | |
518 | } | |
519 | } | |
520 | } | |
521 | ||
522 | // Make sure we've got the right return type. | |
523 | if let Some(t) = rust.ret { | |
3dfed10e XL |
524 | equate(t, &intel.return_.type_, rust.name, false)?; |
525 | } else if intel.return_.type_ != "" && intel.return_.type_ != "void" { | |
0531ce1d XL |
526 | bail!( |
527 | "{} returns `{}` with intel, void in rust", | |
528 | rust.name, | |
3dfed10e | 529 | intel.return_.type_ |
0531ce1d XL |
530 | ) |
531 | } | |
532 | ||
533 | // If there's no arguments on Rust's side intel may list one "void" | |
534 | // argument, so handle that here. | |
535 | if rust.arguments.is_empty() && intel.parameters.len() == 1 { | |
536 | if intel.parameters[0].type_ != "void" { | |
537 | bail!("rust has 0 arguments, intel has one for") | |
538 | } | |
539 | } else { | |
540 | // Otherwise we want all parameters to be exactly the same | |
541 | if rust.arguments.len() != intel.parameters.len() { | |
542 | bail!("wrong number of arguments on {}", rust.name) | |
543 | } | |
0731742a | 544 | for (i, (a, b)) in intel.parameters.iter().zip(rust.arguments).enumerate() { |
0531ce1d XL |
545 | let is_const = rust.required_const.contains(&i); |
546 | equate(b, &a.type_, &intel.name, is_const)?; | |
547 | } | |
548 | } | |
549 | ||
0731742a XL |
550 | let any_i64 = rust |
551 | .arguments | |
552 | .iter() | |
553 | .cloned() | |
554 | .chain(rust.ret) | |
555 | .any(|arg| match *arg { | |
556 | Type::PrimSigned(64) | Type::PrimUnsigned(64) => true, | |
557 | _ => false, | |
558 | }); | |
0531ce1d XL |
559 | let any_i64_exempt = match rust.name { |
560 | // These intrinsics have all been manually verified against Clang's | |
561 | // headers to be available on x86, and the u64 arguments seem | |
562 | // spurious I guess? | |
0731742a XL |
563 | "_xsave" | "_xrstor" | "_xsetbv" | "_xgetbv" | "_xsaveopt" | "_xsavec" | "_xsaves" |
564 | | "_xrstors" => true, | |
0531ce1d XL |
565 | |
566 | // Apparently all of clang/msvc/gcc accept these intrinsics on | |
567 | // 32-bit, so let's do the same | |
0731742a | 568 | "_mm_set_epi64x" | "_mm_set1_epi64x" | "_mm256_set_epi64x" | "_mm256_setr_epi64x" |
9fa01778 | 569 | | "_mm256_set1_epi64x" | "_mm512_set1_epi64" => true, |
0531ce1d XL |
570 | |
571 | // These return a 64-bit argument but they're assembled from other | |
572 | // 32-bit registers, so these work on 32-bit just fine. See #308 for | |
573 | // more info. | |
574 | "_rdtsc" | "__rdtscp" => true, | |
575 | ||
576 | _ => false, | |
577 | }; | |
578 | if any_i64 && !any_i64_exempt && !rust.file.contains("x86_64") { | |
579 | bail!( | |
580 | "intrinsic `{}` uses a 64-bit bare type but may be \ | |
581 | available on 32-bit platforms", | |
582 | rust.name | |
583 | ) | |
584 | } | |
585 | Ok(()) | |
586 | } | |
587 | ||
0731742a | 588 | fn equate(t: &Type, intel: &str, intrinsic: &str, is_const: bool) -> Result<(), String> { |
416331ca XL |
589 | // Make pointer adjacent to the type: float * foo => float* foo |
590 | let mut intel = intel.replace(" *", "*"); | |
591 | // Make mutability modifier adjacent to the pointer: | |
592 | // float const * foo => float const* foo | |
593 | intel = intel.replace("const *", "const*"); | |
594 | // Normalize mutability modifier to after the type: | |
595 | // const float* foo => float const* | |
596 | if intel.starts_with("const") && intel.ends_with("*") { | |
597 | intel = intel.replace("const ", ""); | |
598 | intel = intel.replace("*", " const*"); | |
599 | } | |
0531ce1d XL |
600 | let require_const = || { |
601 | if is_const { | |
602 | return Ok(()); | |
603 | } | |
8faf50e0 | 604 | Err(format!("argument required to be const but isn't")) |
0531ce1d XL |
605 | }; |
606 | match (t, &intel[..]) { | |
607 | (&Type::PrimFloat(32), "float") => {} | |
608 | (&Type::PrimFloat(64), "double") => {} | |
609 | (&Type::PrimSigned(16), "__int16") => {} | |
610 | (&Type::PrimSigned(16), "short") => {} | |
611 | (&Type::PrimSigned(32), "__int32") => {} | |
612 | (&Type::PrimSigned(32), "const int") => require_const()?, | |
613 | (&Type::PrimSigned(32), "int") => {} | |
614 | (&Type::PrimSigned(64), "__int64") => {} | |
615 | (&Type::PrimSigned(64), "long long") => {} | |
616 | (&Type::PrimSigned(8), "__int8") => {} | |
617 | (&Type::PrimSigned(8), "char") => {} | |
618 | (&Type::PrimUnsigned(16), "unsigned short") => {} | |
619 | (&Type::PrimUnsigned(32), "unsigned int") => {} | |
416331ca | 620 | (&Type::PrimUnsigned(32), "const unsigned int") => {} |
0531ce1d XL |
621 | (&Type::PrimUnsigned(64), "unsigned __int64") => {} |
622 | (&Type::PrimUnsigned(8), "unsigned char") => {} | |
416331ca XL |
623 | (&Type::M64, "__m64") => {} |
624 | (&Type::M128, "__m128") => {} | |
625 | (&Type::M128I, "__m128i") => {} | |
626 | (&Type::M128D, "__m128d") => {} | |
627 | (&Type::M256, "__m256") => {} | |
628 | (&Type::M256I, "__m256i") => {} | |
629 | (&Type::M256D, "__m256d") => {} | |
630 | (&Type::M512, "__m512") => {} | |
631 | (&Type::M512I, "__m512i") => {} | |
632 | (&Type::M512D, "__m512d") => {} | |
633 | ||
634 | (&Type::MutPtr(&Type::PrimFloat(32)), "float*") => {} | |
635 | (&Type::MutPtr(&Type::PrimFloat(64)), "double*") => {} | |
3dfed10e XL |
636 | (&Type::MutPtr(&Type::PrimFloat(32)), "void*") => {} |
637 | (&Type::MutPtr(&Type::PrimFloat(64)), "void*") => {} | |
416331ca XL |
638 | (&Type::MutPtr(&Type::PrimSigned(32)), "int*") => {} |
639 | (&Type::MutPtr(&Type::PrimSigned(32)), "__int32*") => {} | |
640 | (&Type::MutPtr(&Type::PrimSigned(64)), "__int64*") => {} | |
641 | (&Type::MutPtr(&Type::PrimSigned(8)), "char*") => {} | |
642 | (&Type::MutPtr(&Type::PrimUnsigned(16)), "unsigned short*") => {} | |
643 | (&Type::MutPtr(&Type::PrimUnsigned(32)), "unsigned int*") => {} | |
644 | (&Type::MutPtr(&Type::PrimUnsigned(64)), "unsigned __int64*") => {} | |
645 | (&Type::MutPtr(&Type::PrimUnsigned(8)), "void*") => {} | |
646 | (&Type::MutPtr(&Type::M64), "__m64*") => {} | |
647 | (&Type::MutPtr(&Type::M128), "__m128*") => {} | |
648 | (&Type::MutPtr(&Type::M128I), "__m128i*") => {} | |
649 | (&Type::MutPtr(&Type::M128D), "__m128d*") => {} | |
650 | (&Type::MutPtr(&Type::M256), "__m256*") => {} | |
651 | (&Type::MutPtr(&Type::M256I), "__m256i*") => {} | |
652 | (&Type::MutPtr(&Type::M256D), "__m256d*") => {} | |
653 | (&Type::MutPtr(&Type::M512), "__m512*") => {} | |
654 | (&Type::MutPtr(&Type::M512I), "__m512i*") => {} | |
655 | (&Type::MutPtr(&Type::M512D), "__m512d*") => {} | |
656 | ||
657 | (&Type::ConstPtr(&Type::PrimFloat(32)), "float const*") => {} | |
658 | (&Type::ConstPtr(&Type::PrimFloat(64)), "double const*") => {} | |
3dfed10e XL |
659 | (&Type::ConstPtr(&Type::PrimFloat(32)), "void const*") => {} |
660 | (&Type::ConstPtr(&Type::PrimFloat(64)), "void const*") => {} | |
416331ca XL |
661 | (&Type::ConstPtr(&Type::PrimSigned(32)), "int const*") => {} |
662 | (&Type::ConstPtr(&Type::PrimSigned(32)), "__int32 const*") => {} | |
663 | (&Type::ConstPtr(&Type::PrimSigned(64)), "__int64 const*") => {} | |
664 | (&Type::ConstPtr(&Type::PrimSigned(8)), "char const*") => {} | |
665 | (&Type::ConstPtr(&Type::PrimUnsigned(16)), "unsigned short const*") => {} | |
666 | (&Type::ConstPtr(&Type::PrimUnsigned(32)), "unsigned int const*") => {} | |
667 | (&Type::ConstPtr(&Type::PrimUnsigned(64)), "unsigned __int64 const*") => {} | |
668 | (&Type::ConstPtr(&Type::PrimUnsigned(8)), "void const*") => {} | |
669 | (&Type::ConstPtr(&Type::M64), "__m64 const*") => {} | |
670 | (&Type::ConstPtr(&Type::M128), "__m128 const*") => {} | |
671 | (&Type::ConstPtr(&Type::M128I), "__m128i const*") => {} | |
672 | (&Type::ConstPtr(&Type::M128D), "__m128d const*") => {} | |
673 | (&Type::ConstPtr(&Type::M256), "__m256 const*") => {} | |
674 | (&Type::ConstPtr(&Type::M256I), "__m256i const*") => {} | |
675 | (&Type::ConstPtr(&Type::M256D), "__m256d const*") => {} | |
676 | (&Type::ConstPtr(&Type::M512), "__m512 const*") => {} | |
677 | (&Type::ConstPtr(&Type::M512I), "__m512i const*") => {} | |
678 | (&Type::ConstPtr(&Type::M512D), "__m512d const*") => {} | |
0731742a | 679 | |
f9f354fc | 680 | (&Type::MMASK8, "__mmask8") => {} |
0731742a | 681 | (&Type::MMASK16, "__mmask16") => {} |
3dfed10e | 682 | (&Type::MM_CMPINT_ENUM, "_MM_CMPINT_ENUM") => {} |
1b1a35ee XL |
683 | (&Type::MM_MANTISSA_NORM_ENUM, "_MM_MANTISSA_NORM_ENUM") => {} |
684 | (&Type::MM_MANTISSA_SIGN_ENUM, "_MM_MANTISSA_SIGN_ENUM") => {} | |
685 | (&Type::MM_PERM_ENUM, "_MM_PERM_ENUM") => {} | |
0731742a | 686 | |
0531ce1d XL |
687 | // This is a macro (?) in C which seems to mutate its arguments, but |
688 | // that means that we're taking pointers to arguments in rust | |
689 | // as we're not exposing it as a macro. | |
416331ca XL |
690 | (&Type::MutPtr(&Type::M128), "__m128") if intrinsic == "_MM_TRANSPOSE4_PS" => {} |
691 | ||
692 | // The _rdtsc intrinsic uses a __int64 return type, but this is a bug in | |
693 | // the intrinsics guide: https://github.com/rust-lang/stdarch/issues/559 | |
694 | // We have manually fixed the bug by changing the return type to `u64`. | |
695 | (&Type::PrimUnsigned(64), "__int64") if intrinsic == "_rdtsc" => {} | |
696 | ||
697 | // The _bittest and _bittest64 intrinsics takes a mutable pointer in the | |
698 | // intrinsics guide even though it never writes through the pointer: | |
699 | (&Type::ConstPtr(&Type::PrimSigned(32)), "__int32*") if intrinsic == "_bittest" => {} | |
700 | (&Type::ConstPtr(&Type::PrimSigned(64)), "__int64*") if intrinsic == "_bittest64" => {} | |
701 | // The _xrstor, _fxrstor, _xrstor64, _fxrstor64 intrinsics take a | |
702 | // mutable pointer in the intrinsics guide even though they never write | |
703 | // through the pointer: | |
704 | (&Type::ConstPtr(&Type::PrimUnsigned(8)), "void*") | |
705 | if intrinsic == "_xrstor" | |
706 | || intrinsic == "_xrstor64" | |
707 | || intrinsic == "_fxrstor" | |
708 | || intrinsic == "_fxrstor64" => {} | |
0531ce1d XL |
709 | |
710 | _ => bail!( | |
711 | "failed to equate: `{}` and {:?} for {}", | |
712 | intel, | |
713 | t, | |
714 | intrinsic | |
715 | ), | |
716 | } | |
717 | Ok(()) | |
718 | } |