mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 13:04:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			989 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			989 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![allow(bad_style)]
 | |
| #![allow(unused)]
 | |
| use std::{collections::HashMap, rc::Rc};
 | |
| 
 | |
| use html5ever::{
 | |
|     driver::ParseOpts,
 | |
|     parse_document,
 | |
|     rcdom::{Node, NodeData, RcDom},
 | |
|     tendril::TendrilSink,
 | |
|     tree_builder::TreeBuilderOpts,
 | |
| };
 | |
| 
 | |
| struct Function {
 | |
|     name: &'static str,
 | |
|     arguments: &'static [&'static Type],
 | |
|     ret: Option<&'static Type>,
 | |
|     target_feature: Option<&'static str>,
 | |
|     instrs: &'static [&'static str],
 | |
|     file: &'static str,
 | |
|     required_const: &'static [usize],
 | |
|     has_test: bool,
 | |
| }
 | |
| 
 | |
| static F16: Type = Type::PrimFloat(16);
 | |
| static F32: Type = Type::PrimFloat(32);
 | |
| static F64: Type = Type::PrimFloat(64);
 | |
| static I16: Type = Type::PrimSigned(16);
 | |
| static I32: Type = Type::PrimSigned(32);
 | |
| static I64: Type = Type::PrimSigned(64);
 | |
| static I8: Type = Type::PrimSigned(8);
 | |
| static U16: Type = Type::PrimUnsigned(16);
 | |
| static U32: Type = Type::PrimUnsigned(32);
 | |
| static U64: Type = Type::PrimUnsigned(64);
 | |
| static U8: Type = Type::PrimUnsigned(8);
 | |
| static NEVER: Type = Type::Never;
 | |
| 
 | |
| static F16X4: Type = Type::F(16, 4, 1);
 | |
| static F16X4X2: Type = Type::F(16, 4, 2);
 | |
| static F16X4X3: Type = Type::F(16, 4, 3);
 | |
| static F16X4X4: Type = Type::F(16, 4, 4);
 | |
| static F16X8: Type = Type::F(16, 8, 1);
 | |
| static F16X8X2: Type = Type::F(16, 8, 2);
 | |
| static F16X8X3: Type = Type::F(16, 8, 3);
 | |
| static F16X8X4: Type = Type::F(16, 8, 4);
 | |
| static F32X2: Type = Type::F(32, 2, 1);
 | |
| static F32X2X2: Type = Type::F(32, 2, 2);
 | |
| static F32X2X3: Type = Type::F(32, 2, 3);
 | |
| static F32X2X4: Type = Type::F(32, 2, 4);
 | |
| static F32X4: Type = Type::F(32, 4, 1);
 | |
| static F32X4X2: Type = Type::F(32, 4, 2);
 | |
| static F32X4X3: Type = Type::F(32, 4, 3);
 | |
| static F32X4X4: Type = Type::F(32, 4, 4);
 | |
| static F64X1: Type = Type::F(64, 1, 1);
 | |
| static F64X1X2: Type = Type::F(64, 1, 2);
 | |
| static F64X1X3: Type = Type::F(64, 1, 3);
 | |
| static F64X1X4: Type = Type::F(64, 1, 4);
 | |
| static F64X2: Type = Type::F(64, 2, 1);
 | |
| static F64X2X2: Type = Type::F(64, 2, 2);
 | |
| static F64X2X3: Type = Type::F(64, 2, 3);
 | |
| static F64X2X4: Type = Type::F(64, 2, 4);
 | |
| static I16X2: Type = Type::I(16, 2, 1);
 | |
| static I16X4: Type = Type::I(16, 4, 1);
 | |
| static I16X4X2: Type = Type::I(16, 4, 2);
 | |
| static I16X4X3: Type = Type::I(16, 4, 3);
 | |
| static I16X4X4: Type = Type::I(16, 4, 4);
 | |
| static I16X8: Type = Type::I(16, 8, 1);
 | |
| static I16X8X2: Type = Type::I(16, 8, 2);
 | |
| static I16X8X3: Type = Type::I(16, 8, 3);
 | |
| static I16X8X4: Type = Type::I(16, 8, 4);
 | |
| static I32X2: Type = Type::I(32, 2, 1);
 | |
| static I32X2X2: Type = Type::I(32, 2, 2);
 | |
| static I32X2X3: Type = Type::I(32, 2, 3);
 | |
| static I32X2X4: Type = Type::I(32, 2, 4);
 | |
| static I32X4: Type = Type::I(32, 4, 1);
 | |
| static I32X4X2: Type = Type::I(32, 4, 2);
 | |
| static I32X4X3: Type = Type::I(32, 4, 3);
 | |
| static I32X4X4: Type = Type::I(32, 4, 4);
 | |
| static I64X1: Type = Type::I(64, 1, 1);
 | |
| static I64X1X2: Type = Type::I(64, 1, 2);
 | |
| static I64X1X3: Type = Type::I(64, 1, 3);
 | |
| static I64X1X4: Type = Type::I(64, 1, 4);
 | |
| static I64X2: Type = Type::I(64, 2, 1);
 | |
| static I64X2X2: Type = Type::I(64, 2, 2);
 | |
| static I64X2X3: Type = Type::I(64, 2, 3);
 | |
| static I64X2X4: Type = Type::I(64, 2, 4);
 | |
| static I8X16: Type = Type::I(8, 16, 1);
 | |
| static I8X16X2: Type = Type::I(8, 16, 2);
 | |
| static I8X16X3: Type = Type::I(8, 16, 3);
 | |
| static I8X16X4: Type = Type::I(8, 16, 4);
 | |
| static I8X4: Type = Type::I(8, 4, 1);
 | |
| static I8X8: Type = Type::I(8, 8, 1);
 | |
| static I8X8X2: Type = Type::I(8, 8, 2);
 | |
| static I8X8X3: Type = Type::I(8, 8, 3);
 | |
| static I8X8X4: Type = Type::I(8, 8, 4);
 | |
| static P128: Type = Type::PrimPoly(128);
 | |
| static P16: Type = Type::PrimPoly(16);
 | |
| static P16X4X2: Type = Type::P(16, 4, 2);
 | |
| static P16X4X3: Type = Type::P(16, 4, 3);
 | |
| static P16X4X4: Type = Type::P(16, 4, 4);
 | |
| static P16X8X2: Type = Type::P(16, 8, 2);
 | |
| static P16X8X3: Type = Type::P(16, 8, 3);
 | |
| static P16X8X4: Type = Type::P(16, 8, 4);
 | |
| static P64: Type = Type::PrimPoly(64);
 | |
| static P64X1X2: Type = Type::P(64, 1, 2);
 | |
| static P64X1X3: Type = Type::P(64, 1, 3);
 | |
| static P64X1X4: Type = Type::P(64, 1, 4);
 | |
| static P64X2X2: Type = Type::P(64, 2, 2);
 | |
| static P64X2X3: Type = Type::P(64, 2, 3);
 | |
| static P64X2X4: Type = Type::P(64, 2, 4);
 | |
| static P8: Type = Type::PrimPoly(8);
 | |
| static POLY16X4: Type = Type::P(16, 4, 1);
 | |
| static POLY16X8: Type = Type::P(16, 8, 1);
 | |
| static POLY64X1: Type = Type::P(64, 1, 1);
 | |
| static POLY64X2: Type = Type::P(64, 2, 1);
 | |
| static POLY8X16: Type = Type::P(8, 16, 1);
 | |
| static POLY8X16X2: Type = Type::P(8, 16, 2);
 | |
| static POLY8X16X3: Type = Type::P(8, 16, 3);
 | |
| static POLY8X16X4: Type = Type::P(8, 16, 4);
 | |
| static POLY8X8: Type = Type::P(8, 8, 1);
 | |
| static POLY8X8X2: Type = Type::P(8, 8, 2);
 | |
| static POLY8X8X3: Type = Type::P(8, 8, 3);
 | |
| static POLY8X8X4: Type = Type::P(8, 8, 4);
 | |
| static U16X4: Type = Type::U(16, 4, 1);
 | |
| static U16X4X2: Type = Type::U(16, 4, 2);
 | |
| static U16X4X3: Type = Type::U(16, 4, 3);
 | |
| static U16X4X4: Type = Type::U(16, 4, 4);
 | |
| static U16X8: Type = Type::U(16, 8, 1);
 | |
| static U16X8X2: Type = Type::U(16, 8, 2);
 | |
| static U16X8X3: Type = Type::U(16, 8, 3);
 | |
| static U16X8X4: Type = Type::U(16, 8, 4);
 | |
| static U32X2: Type = Type::U(32, 2, 1);
 | |
| static U32X2X2: Type = Type::U(32, 2, 2);
 | |
| static U32X2X3: Type = Type::U(32, 2, 3);
 | |
| static U32X2X4: Type = Type::U(32, 2, 4);
 | |
| static U32X4: Type = Type::U(32, 4, 1);
 | |
| static U32X4X2: Type = Type::U(32, 4, 2);
 | |
| static U32X4X3: Type = Type::U(32, 4, 3);
 | |
| static U32X4X4: Type = Type::U(32, 4, 4);
 | |
| static U64X1: Type = Type::U(64, 1, 1);
 | |
| static U64X1X2: Type = Type::U(64, 1, 2);
 | |
| static U64X1X3: Type = Type::U(64, 1, 3);
 | |
| static U64X1X4: Type = Type::U(64, 1, 4);
 | |
| static U64X2: Type = Type::U(64, 2, 1);
 | |
| static U64X2X2: Type = Type::U(64, 2, 2);
 | |
| static U64X2X3: Type = Type::U(64, 2, 3);
 | |
| static U64X2X4: Type = Type::U(64, 2, 4);
 | |
| static U8X16: Type = Type::U(8, 16, 1);
 | |
| static U8X16X2: Type = Type::U(8, 16, 2);
 | |
| static U8X16X3: Type = Type::U(8, 16, 3);
 | |
| static U8X16X4: Type = Type::U(8, 16, 4);
 | |
| static U8X8: Type = Type::U(8, 8, 1);
 | |
| static U8X4: Type = Type::U(8, 4, 1);
 | |
| static U8X8X2: Type = Type::U(8, 8, 2);
 | |
| static U8X8X3: Type = Type::U(8, 8, 3);
 | |
| static U8X8X4: Type = Type::U(8, 8, 4);
 | |
| 
 | |
| #[derive(Debug, Copy, Clone, PartialEq)]
 | |
| enum Type {
 | |
|     PrimFloat(u8),
 | |
|     PrimSigned(u8),
 | |
|     PrimUnsigned(u8),
 | |
|     PrimPoly(u8),
 | |
|     MutPtr(&'static Type),
 | |
|     ConstPtr(&'static Type),
 | |
|     I(u8, u8, u8),
 | |
|     U(u8, u8, u8),
 | |
|     P(u8, u8, u8),
 | |
|     F(u8, u8, u8),
 | |
|     Never,
 | |
| }
 | |
| 
 | |
| stdarch_verify::arm_functions!(static FUNCTIONS);
 | |
| 
 | |
| macro_rules! bail {
 | |
|     ($($t:tt)*) => (return Err(format!($($t)*)))
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn verify_all_signatures() {
 | |
|     // This is a giant HTML blob downloaded from
 | |
|     // https://developer.arm.com/technologies/neon/intrinsics which contains all
 | |
|     // NEON intrinsics at least. We do manual HTML parsing below.
 | |
|     let html = include_bytes!("../arm-intrinsics.html");
 | |
|     let mut html = &html[..];
 | |
|     let opts = ParseOpts {
 | |
|         tree_builder: TreeBuilderOpts {
 | |
|             drop_doctype: true,
 | |
|             ..Default::default()
 | |
|         },
 | |
|         ..Default::default()
 | |
|     };
 | |
|     let dom = parse_document(RcDom::default(), opts)
 | |
|         .from_utf8()
 | |
|         .read_from(&mut html)
 | |
|         .unwrap();
 | |
| 
 | |
|     let accordion = find_accordion(&dom.document).unwrap();
 | |
|     let map = parse_intrinsics(&accordion);
 | |
| 
 | |
|     let mut all_valid = true;
 | |
|     'outer: for rust in FUNCTIONS {
 | |
|         if !rust.has_test {
 | |
|             let skip = [
 | |
|                 "vaddq_s64",
 | |
|                 "vaddq_u64",
 | |
|                 "vrsqrte_f32",
 | |
|                 "vtbl1_s8",
 | |
|                 "vtbl1_u8",
 | |
|                 "vtbl1_p8",
 | |
|                 "vtbl2_s8",
 | |
|                 "vtbl2_u8",
 | |
|                 "vtbl2_p8",
 | |
|                 "vtbl3_s8",
 | |
|                 "vtbl3_u8",
 | |
|                 "vtbl3_p8",
 | |
|                 "vtbl4_s8",
 | |
|                 "vtbl4_u8",
 | |
|                 "vtbl4_p8",
 | |
|                 "vtbx1_s8",
 | |
|                 "vtbx1_u8",
 | |
|                 "vtbx1_p8",
 | |
|                 "vtbx2_s8",
 | |
|                 "vtbx2_u8",
 | |
|                 "vtbx2_p8",
 | |
|                 "vtbx3_s8",
 | |
|                 "vtbx3_u8",
 | |
|                 "vtbx3_p8",
 | |
|                 "vtbx4_s8",
 | |
|                 "vtbx4_u8",
 | |
|                 "vtbx4_p8",
 | |
|                 "udf",
 | |
|                 "_clz_u8",
 | |
|                 "_clz_u16",
 | |
|                 "_clz_u32",
 | |
|                 "_rbit_u32",
 | |
|                 "_rev_u16",
 | |
|                 "_rev_u32",
 | |
|                 "__breakpoint",
 | |
|                 "vpminq_f32",
 | |
|                 "vpminq_f64",
 | |
|                 "vpmaxq_f32",
 | |
|                 "vpmaxq_f64",
 | |
|                 "vcombine_s8",
 | |
|                 "vcombine_s16",
 | |
|                 "vcombine_s32",
 | |
|                 "vcombine_s64",
 | |
|                 "vcombine_u8",
 | |
|                 "vcombine_u16",
 | |
|                 "vcombine_u32",
 | |
|                 "vcombine_u64",
 | |
|                 "vcombine_p64",
 | |
|                 "vcombine_f32",
 | |
|                 "vcombine_p8",
 | |
|                 "vcombine_p16",
 | |
|                 "vcombine_f64",
 | |
|                 "vtbl1_s8",
 | |
|                 "vtbl1_u8",
 | |
|                 "vtbl1_p8",
 | |
|                 "vtbl2_s8",
 | |
|                 "vtbl2_u8",
 | |
|                 "vtbl2_p8",
 | |
|                 "vtbl3_s8",
 | |
|                 "vtbl3_u8",
 | |
|                 "vtbl3_p8",
 | |
|                 "vtbl4_s8",
 | |
|                 "vtbl4_u8",
 | |
|                 "vtbl4_p8",
 | |
|                 "vtbx1_s8",
 | |
|                 "vtbx1_u8",
 | |
|                 "vtbx1_p8",
 | |
|                 "vtbx2_s8",
 | |
|                 "vtbx2_u8",
 | |
|                 "vtbx2_p8",
 | |
|                 "vtbx3_s8",
 | |
|                 "vtbx3_u8",
 | |
|                 "vtbx3_p8",
 | |
|                 "vtbx4_s8",
 | |
|                 "vtbx4_u8",
 | |
|                 "vtbx4_p8",
 | |
|                 "vqtbl1_s8",
 | |
|                 "vqtbl1q_s8",
 | |
|                 "vqtbl1_u8",
 | |
|                 "vqtbl1q_u8",
 | |
|                 "vqtbl1_p8",
 | |
|                 "vqtbl1q_p8",
 | |
|                 "vqtbx1_s8",
 | |
|                 "vqtbx1q_s8",
 | |
|                 "vqtbx1_u8",
 | |
|                 "vqtbx1q_u8",
 | |
|                 "vqtbx1_p8",
 | |
|                 "vqtbx1q_p8",
 | |
|                 "vqtbl2_s8",
 | |
|                 "vqtbl2q_s8",
 | |
|                 "vqtbl2_u8",
 | |
|                 "vqtbl2q_u8",
 | |
|                 "vqtbl2_p8",
 | |
|                 "vqtbl2q_p8",
 | |
|                 "vqtbx2_s8",
 | |
|                 "vqtbx2q_s8",
 | |
|                 "vqtbx2_u8",
 | |
|                 "vqtbx2q_u8",
 | |
|                 "vqtbx2_p8",
 | |
|                 "vqtbx2q_p8",
 | |
|                 "vqtbl3_s8",
 | |
|                 "vqtbl3q_s8",
 | |
|                 "vqtbl3_u8",
 | |
|                 "vqtbl3q_u8",
 | |
|                 "vqtbl3_p8",
 | |
|                 "vqtbl3q_p8",
 | |
|                 "vqtbx3_s8",
 | |
|                 "vqtbx3q_s8",
 | |
|                 "vqtbx3_u8",
 | |
|                 "vqtbx3q_u8",
 | |
|                 "vqtbx3_p8",
 | |
|                 "vqtbx3q_p8",
 | |
|                 "vqtbl4_s8",
 | |
|                 "vqtbl4q_s8",
 | |
|                 "vqtbl4_u8",
 | |
|                 "vqtbl4q_u8",
 | |
|                 "vqtbl4_p8",
 | |
|                 "vqtbl4q_p8",
 | |
|                 "vqtbx4_s8",
 | |
|                 "vqtbx4q_s8",
 | |
|                 "vqtbx4_u8",
 | |
|                 "vqtbx4q_u8",
 | |
|                 "vqtbx4_p8",
 | |
|                 "vqtbx4q_p8",
 | |
|                 "brk",
 | |
|                 "_rev_u64",
 | |
|                 "_clz_u64",
 | |
|                 "_rbit_u64",
 | |
|                 "_cls_u32",
 | |
|                 "_cls_u64",
 | |
|                 "_prefetch",
 | |
|                 "vsli_n_s8",
 | |
|                 "vsliq_n_s8",
 | |
|                 "vsli_n_s16",
 | |
|                 "vsliq_n_s16",
 | |
|                 "vsli_n_s32",
 | |
|                 "vsliq_n_s32",
 | |
|                 "vsli_n_s64",
 | |
|                 "vsliq_n_s64",
 | |
|                 "vsli_n_u8",
 | |
|                 "vsliq_n_u8",
 | |
|                 "vsli_n_u16",
 | |
|                 "vsliq_n_u16",
 | |
|                 "vsli_n_u32",
 | |
|                 "vsliq_n_u32",
 | |
|                 "vsli_n_u64",
 | |
|                 "vsliq_n_u64",
 | |
|                 "vsli_n_p8",
 | |
|                 "vsliq_n_p8",
 | |
|                 "vsli_n_p16",
 | |
|                 "vsliq_n_p16",
 | |
|                 "vsli_n_p64",
 | |
|                 "vsliq_n_p64",
 | |
|                 "vsri_n_s8",
 | |
|                 "vsriq_n_s8",
 | |
|                 "vsri_n_s16",
 | |
|                 "vsriq_n_s16",
 | |
|                 "vsri_n_s32",
 | |
|                 "vsriq_n_s32",
 | |
|                 "vsri_n_s64",
 | |
|                 "vsriq_n_s64",
 | |
|                 "vsri_n_u8",
 | |
|                 "vsriq_n_u8",
 | |
|                 "vsri_n_u16",
 | |
|                 "vsriq_n_u16",
 | |
|                 "vsri_n_u32",
 | |
|                 "vsriq_n_u32",
 | |
|                 "vsri_n_u64",
 | |
|                 "vsriq_n_u64",
 | |
|                 "vsri_n_p8",
 | |
|                 "vsriq_n_p8",
 | |
|                 "vsri_n_p16",
 | |
|                 "vsriq_n_p16",
 | |
|                 "vsri_n_p64",
 | |
|                 "vsriq_n_p64",
 | |
|                 "__smulbb",
 | |
|                 "__smultb",
 | |
|                 "__smulbt",
 | |
|                 "__smultt",
 | |
|                 "__smulwb",
 | |
|                 "__smulwt",
 | |
|                 "__qadd",
 | |
|                 "__qsub",
 | |
|                 "__qdbl",
 | |
|                 "__smlabb",
 | |
|                 "__smlabt",
 | |
|                 "__smlatb",
 | |
|                 "__smlatt",
 | |
|                 "__smlawb",
 | |
|                 "__smlawt",
 | |
|                 "__qadd8",
 | |
|                 "__qsub8",
 | |
|                 "__qsub16",
 | |
|                 "__qadd16",
 | |
|                 "__qasx",
 | |
|                 "__qsax",
 | |
|                 "__sadd16",
 | |
|                 "__sadd8",
 | |
|                 "__smlad",
 | |
|                 "__smlsd",
 | |
|                 "__sasx",
 | |
|                 "__sel",
 | |
|                 "__shadd8",
 | |
|                 "__shadd16",
 | |
|                 "__shsub8",
 | |
|                 "__usub8",
 | |
|                 "__ssub8",
 | |
|                 "__shsub16",
 | |
|                 "__smuad",
 | |
|                 "__smuadx",
 | |
|                 "__smusd",
 | |
|                 "__smusdx",
 | |
|                 "__usad8",
 | |
|                 "__usada8",
 | |
|                 "__ldrex",
 | |
|                 "__strex",
 | |
|                 "__ldrexb",
 | |
|                 "__strexb",
 | |
|                 "__ldrexh",
 | |
|                 "__strexh",
 | |
|                 "__clrex",
 | |
|                 "__dbg",
 | |
|             ];
 | |
|             if !skip.contains(&rust.name) {
 | |
|                 println!(
 | |
|                     "missing run-time test named `test_{}` for `{}`",
 | |
|                     {
 | |
|                         let mut id = rust.name;
 | |
|                         while id.starts_with('_') {
 | |
|                             id = &id[1..];
 | |
|                         }
 | |
|                         id
 | |
|                     },
 | |
|                     rust.name
 | |
|                 );
 | |
|                 all_valid = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Skip some intrinsics that aren't NEON and are located in different
 | |
|         // places than the whitelists below.
 | |
|         match rust.name {
 | |
|             "brk" | "__breakpoint" | "udf" | "_prefetch" => continue,
 | |
|             _ => {}
 | |
|         }
 | |
|         // Skip some intrinsics that are present in GCC and Clang but
 | |
|         // are missing from the official documentation.
 | |
|         let skip_intrinsic_verify = [
 | |
|             "vmov_n_p64",
 | |
|             "vmovq_n_p64",
 | |
|             "vreinterpret_p64_s64",
 | |
|             "vreinterpret_f32_p64",
 | |
|             "vreinterpretq_f32_p64",
 | |
|             "vreinterpretq_p64_p128",
 | |
|             "vreinterpretq_p128_p64",
 | |
|             "vreinterpretq_f32_p128",
 | |
|             "vqrdmlahh_s16",
 | |
|             "vqrdmlahs_s32",
 | |
|             "vqrdmlahh_lane_s16",
 | |
|             "vqrdmlahh_laneq_s16",
 | |
|             "vqrdmlahs_lane_s32",
 | |
|             "vqrdmlahs_laneq_s32",
 | |
|             "vqrdmlah_s16",
 | |
|             "vqrdmlah_s32",
 | |
|             "vqrdmlahq_s16",
 | |
|             "vqrdmlahq_s32",
 | |
|             "vqrdmlah_lane_s16",
 | |
|             "vqrdmlah_laneq_s16",
 | |
|             "vqrdmlahq_lane_s16",
 | |
|             "vqrdmlahq_laneq_s16",
 | |
|             "vqrdmlah_lane_s32",
 | |
|             "vqrdmlah_laneq_s32",
 | |
|             "vqrdmlahq_lane_s32",
 | |
|             "vqrdmlahq_laneq_s32",
 | |
|             "vqrdmlshh_s16",
 | |
|             "vqrdmlshs_s32",
 | |
|             "vqrdmlshh_lane_s16",
 | |
|             "vqrdmlshh_laneq_s16",
 | |
|             "vqrdmlshs_lane_s32",
 | |
|             "vqrdmlshs_laneq_s32",
 | |
|             "vqrdmlsh_s16",
 | |
|             "vqrdmlshq_s16",
 | |
|             "vqrdmlsh_s32",
 | |
|             "vqrdmlshq_s32",
 | |
|             "vqrdmlsh_lane_s16",
 | |
|             "vqrdmlsh_laneq_s16",
 | |
|             "vqrdmlshq_lane_s16",
 | |
|             "vqrdmlshq_laneq_s16",
 | |
|             "vqrdmlsh_lane_s32",
 | |
|             "vqrdmlsh_laneq_s32",
 | |
|             "vqrdmlshq_lane_s32",
 | |
|             "vqrdmlshq_laneq_s32",
 | |
|             "vcadd_rot270_f32",
 | |
|             "vcadd_rot90_f32",
 | |
|             "vcaddq_rot270_f32",
 | |
|             "vcaddq_rot270_f64",
 | |
|             "vcaddq_rot90_f32",
 | |
|             "vcaddq_rot90_f64",
 | |
|             "vcmla_f32",
 | |
|             "vcmlaq_f32",
 | |
|             "vcmlaq_f64",
 | |
|             "vcmla_rot90_f32",
 | |
|             "vcmlaq_rot90_f32",
 | |
|             "vcmlaq_rot90_f64",
 | |
|             "vcmla_rot180_f32",
 | |
|             "vcmlaq_rot180_f32",
 | |
|             "vcmlaq_rot180_f64",
 | |
|             "vcmla_rot270_f32",
 | |
|             "vcmlaq_rot270_f32",
 | |
|             "vcmlaq_rot270_f64",
 | |
|             "vcmla_lane_f32",
 | |
|             "vcmla_laneq_f32",
 | |
|             "vcmlaq_lane_f32",
 | |
|             "vcmlaq_laneq_f32",
 | |
|             "vcmla_rot90_lane_f32",
 | |
|             "vcmla_rot90_laneq_f32",
 | |
|             "vcmlaq_rot90_lane_f32",
 | |
|             "vcmlaq_rot90_laneq_f32",
 | |
|             "vcmla_rot180_lane_f32",
 | |
|             "vcmla_rot180_laneq_f32",
 | |
|             "vcmlaq_rot180_lane_f32",
 | |
|             "vcmlaq_rot180_laneq_f32",
 | |
|             "vcmla_rot270_lane_f32",
 | |
|             "vcmla_rot270_laneq_f32",
 | |
|             "vcmlaq_rot270_lane_f32",
 | |
|             "vcmlaq_rot270_laneq_f32",
 | |
|             "vdot_s32",
 | |
|             "vdot_u32",
 | |
|             "vdotq_s32",
 | |
|             "vdotq_u32",
 | |
|             "vdot_lane_s32",
 | |
|             "vdot_laneq_s32",
 | |
|             "vdotq_lane_s32",
 | |
|             "vdotq_laneq_s32",
 | |
|             "vdot_lane_u32",
 | |
|             "vdot_laneq_u32",
 | |
|             "vdotq_lane_u32",
 | |
|             "vdotq_laneq_u32",
 | |
|             "vbcaxq_s8",
 | |
|             "vbcaxq_s16",
 | |
|             "vbcaxq_s32",
 | |
|             "vbcaxq_s64",
 | |
|             "vbcaxq_u8",
 | |
|             "vbcaxq_u16",
 | |
|             "vbcaxq_u32",
 | |
|             "vbcaxq_u64",
 | |
|             "veor3q_s8",
 | |
|             "veor3q_s16",
 | |
|             "veor3q_s32",
 | |
|             "veor3q_s64",
 | |
|             "veor3q_u8",
 | |
|             "veor3q_u16",
 | |
|             "veor3q_u32",
 | |
|             "veor3q_u64",
 | |
|             "vadd_p8",
 | |
|             "vadd_p16",
 | |
|             "vadd_p64",
 | |
|             "vaddq_p8",
 | |
|             "vaddq_p16",
 | |
|             "vaddq_p64",
 | |
|             "vaddq_p128",
 | |
|             "vsm4ekeyq_u32",
 | |
|             "vsm4eq_u32",
 | |
|             "vmmlaq_s32",
 | |
|             "vmmlaq_u32",
 | |
|             "vusmmlaq_s32",
 | |
|             "vsm3partw1q_u32",
 | |
|             "vsm3partw2q_u32",
 | |
|             "vsm3ss1q_u32",
 | |
|             "vsm3tt1aq_u32",
 | |
|             "vsm3tt1bq_u32",
 | |
|             "vsm3tt2aq_u32",
 | |
|             "vsm3tt2bq_u32",
 | |
|             "vrax1q_u64",
 | |
|             "vxarq_u64",
 | |
|             "vsha512hq_u64",
 | |
|             "vsha512h2q_u64",
 | |
|             "vsha512su0q_u64",
 | |
|             "vsha512su1q_u64",
 | |
|             "vrnd32x_f32",
 | |
|             "vrnd32xq_f32",
 | |
|             "vrnd32z_f32",
 | |
|             "vrnd32zq_f32",
 | |
|             "vrnd64x_f32",
 | |
|             "vrnd64xq_f32",
 | |
|             "vrnd64z_f32",
 | |
|             "vrnd64zq_f32",
 | |
|             "vcls_u8",
 | |
|             "vcls_u16",
 | |
|             "vcls_u32",
 | |
|             "vclsq_u8",
 | |
|             "vclsq_u16",
 | |
|             "vclsq_u32",
 | |
|             "vtst_p16",
 | |
|             "vtstq_p16",
 | |
|             "__dbg",
 | |
|         ];
 | |
|         let arm = match map.get(rust.name) {
 | |
|             Some(i) => i,
 | |
|             None => {
 | |
|                 // Skip all these intrinsics as they're not listed in NEON
 | |
|                 // descriptions online.
 | |
|                 //
 | |
|                 // TODO: we still need to verify these intrinsics or find a
 | |
|                 // reference for them, need to figure out where though!
 | |
|                 if !rust.file.ends_with("dsp.rs\"")
 | |
|                     && !rust.file.ends_with("simd32.rs\"")
 | |
|                     && !rust.file.ends_with("cmsis.rs\"")
 | |
|                     && !rust.file.ends_with("v6.rs\"")
 | |
|                     && !rust.file.ends_with("v7.rs\"")
 | |
|                     && !rust.file.ends_with("v8.rs\"")
 | |
|                     && !rust.file.ends_with("tme.rs\"")
 | |
|                     && !rust.file.ends_with("ex.rs\"")
 | |
|                     && !skip_intrinsic_verify.contains(&rust.name)
 | |
|                 {
 | |
|                     println!(
 | |
|                         "missing arm definition for {:?} in {}",
 | |
|                         rust.name, rust.file
 | |
|                     );
 | |
|                     all_valid = false;
 | |
|                 }
 | |
|                 continue;
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         if let Err(e) = matches(rust, arm) {
 | |
|             println!("failed to verify `{}`", rust.name);
 | |
|             println!("  * {}", e);
 | |
|             all_valid = false;
 | |
|         }
 | |
|     }
 | |
|     assert!(all_valid);
 | |
| }
 | |
| 
 | |
| fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
 | |
|     if rust.ret != arm.ret.as_ref() {
 | |
|         bail!("mismatched return value")
 | |
|     }
 | |
|     if rust.arguments.len() != arm.arguments.len() {
 | |
|         bail!("mismatched argument lengths");
 | |
|     }
 | |
| 
 | |
|     let mut nconst = 0;
 | |
|     let iter = rust.arguments.iter().zip(&arm.arguments).enumerate();
 | |
|     for (i, (rust_ty, (arm, arm_const))) in iter {
 | |
|         if *rust_ty != arm {
 | |
|             bail!("mismatched arguments")
 | |
|         }
 | |
|         if *arm_const {
 | |
|             nconst += 1;
 | |
|             if !rust.required_const.contains(&i) {
 | |
|                 bail!("argument const mismatch");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     if nconst != rust.required_const.len() {
 | |
|         bail!("wrong number of const arguments");
 | |
|     }
 | |
| 
 | |
|     if rust.instrs.is_empty() {
 | |
|         bail!(
 | |
|             "instruction not listed for `{}`, but arm lists {:?}",
 | |
|             rust.name,
 | |
|             arm.instruction
 | |
|         );
 | |
|     } else if false
 | |
|     /* not super reliable, but can be used to manually check */
 | |
|     {
 | |
|         for instr in rust.instrs {
 | |
|             if arm.instruction.starts_with(instr) {
 | |
|                 continue;
 | |
|             }
 | |
|             // sometimes arm says `foo` and disassemblers say `vfoo`, or
 | |
|             // sometimes disassemblers say `vfoo` and arm says `sfoo` or `ffoo`
 | |
|             if instr.starts_with('v')
 | |
|                 && (arm.instruction.starts_with(&instr[1..])
 | |
|                     || arm.instruction[1..].starts_with(&instr[1..]))
 | |
|             {
 | |
|                 continue;
 | |
|             }
 | |
|             bail!(
 | |
|                 "arm failed to list `{}` as an instruction for `{}` in {:?}",
 | |
|                 instr,
 | |
|                 rust.name,
 | |
|                 arm.instruction,
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // TODO: verify `target_feature`.
 | |
| 
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| fn find_accordion(node: &Rc<Node>) -> Option<Rc<Node>> {
 | |
|     if let NodeData::Element { attrs, .. } = &node.data {
 | |
|         for attr in attrs.borrow().iter() {
 | |
|             if attr.name.local.eq_str_ignore_ascii_case("class")
 | |
|                 && attr.value.to_string() == "intrinsic-accordion"
 | |
|             {
 | |
|                 return Some(node.clone());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     node.children
 | |
|         .borrow()
 | |
|         .iter()
 | |
|         .filter_map(|node| find_accordion(node))
 | |
|         .next()
 | |
| }
 | |
| 
 | |
| #[derive(PartialEq)]
 | |
| struct Intrinsic {
 | |
|     name: String,
 | |
|     ret: Option<Type>,
 | |
|     arguments: Vec<(Type, bool)>,
 | |
|     instruction: String,
 | |
| }
 | |
| 
 | |
| fn parse_intrinsics(node: &Rc<Node>) -> HashMap<String, Intrinsic> {
 | |
|     let mut ret = HashMap::new();
 | |
|     for child in node.children.borrow().iter() {
 | |
|         if let NodeData::Element { .. } = child.data {
 | |
|             let f = parse_intrinsic(child);
 | |
|             ret.insert(f.name.clone(), f);
 | |
|         }
 | |
|     }
 | |
|     ret
 | |
| }
 | |
| 
 | |
| fn parse_intrinsic(node: &Rc<Node>) -> Intrinsic {
 | |
|     // <div class='intrinsic'>
 | |
|     //  <input>...</input>
 | |
|     //  <label for=$name>
 | |
|     //    <div>
 | |
|     //      $signature...
 | |
|     //  <article>
 | |
|     //    ...
 | |
| 
 | |
|     let children = node.children.borrow();
 | |
|     let mut children = children
 | |
|         .iter()
 | |
|         .filter(|node| matches!(node.data, NodeData::Element { .. }));
 | |
|     let _input = children.next().expect("no <input>");
 | |
|     let label = children.next().expect("no <label>");
 | |
|     let article = children.next().expect("no <article>");
 | |
|     assert!(children.next().is_none());
 | |
| 
 | |
|     // Find `for="..."` in `<label>`
 | |
|     let name = match &label.data {
 | |
|         NodeData::Element { attrs, .. } => attrs
 | |
|             .borrow()
 | |
|             .iter()
 | |
|             .filter(|attr| attr.name.local.eq_str_ignore_ascii_case("for"))
 | |
|             .map(|attr| attr.value.to_string())
 | |
|             .next()
 | |
|             .expect("no `for` attribute"),
 | |
|         _ => panic!(),
 | |
|     };
 | |
| 
 | |
|     // Find contents of inner `<div>` in `<label>`
 | |
|     let label_children = label.children.borrow();
 | |
|     let mut label_children = label_children
 | |
|         .iter()
 | |
|         .filter(|node| matches!(node.data, NodeData::Element { .. }));
 | |
|     let label_div = label_children.next().expect("no <div> in <label>");
 | |
|     assert!(label_children.next().is_none());
 | |
|     let text = label_div.children.borrow();
 | |
|     let mut text = text.iter().filter_map(|node| match &node.data {
 | |
|         NodeData::Text { contents } => Some(contents.borrow().to_string()),
 | |
|         _ => None,
 | |
|     });
 | |
|     let ret = text.next().unwrap();
 | |
|     let ret = ret.trim();
 | |
|     let args = text.next().unwrap();
 | |
|     let args = args.trim();
 | |
|     assert!(text.next().is_none());
 | |
| 
 | |
|     // Find the instruction within the article
 | |
|     let article_children = article.children.borrow();
 | |
|     let mut article_children = article_children
 | |
|         .iter()
 | |
|         .filter(|node| matches!(node.data, NodeData::Element { .. }));
 | |
|     let mut instruction = None;
 | |
|     while let Some(child) = article_children.next() {
 | |
|         let mut header = String::new();
 | |
|         collect_text(&mut header, child);
 | |
|         if !header.ends_with(" Instruction") {
 | |
|             continue;
 | |
|         }
 | |
|         let next = article_children.next().expect("no next child");
 | |
|         assert!(instruction.is_none());
 | |
|         let mut instr = String::new();
 | |
|         collect_text(&mut instr, &next);
 | |
|         instruction = Some(instr);
 | |
|     }
 | |
| 
 | |
|     let instruction = match instruction {
 | |
|         Some(s) => s.trim().to_lowercase(),
 | |
|         None => panic!("can't find instruction for `{}`", name),
 | |
|     };
 | |
| 
 | |
|     Intrinsic {
 | |
|         name,
 | |
|         ret: if ret == "void" {
 | |
|             None
 | |
|         } else {
 | |
|             Some(parse_ty(ret))
 | |
|         },
 | |
|         instruction,
 | |
|         arguments: args // "(...)"
 | |
|             .trim_start_matches('(') // "...)"
 | |
|             .trim_end_matches(')') // "..."
 | |
|             .split(',') // " Type name ", ".."
 | |
|             .map(|s| s.trim()) // "Type name"
 | |
|             .map(|s| s.rsplitn(2, ' ').nth(1).unwrap()) // "Type"
 | |
|             .map(|s| {
 | |
|                 let const_ = "const ";
 | |
|                 if s.starts_with(const_) {
 | |
|                     (parse_ty(&s[const_.len()..]), true)
 | |
|                 } else {
 | |
|                     (parse_ty(s), false)
 | |
|                 }
 | |
|             })
 | |
|             .collect(),
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn parse_ty(s: &str) -> Type {
 | |
|     let suffix = " const *";
 | |
|     if s.ends_with(suffix) {
 | |
|         Type::ConstPtr(parse_ty_base(&s[..s.len() - suffix.len()]))
 | |
|     } else if s.ends_with(" *") {
 | |
|         Type::MutPtr(parse_ty_base(&s[..s.len() - 2]))
 | |
|     } else {
 | |
|         *parse_ty_base(s)
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn parse_ty_base(s: &str) -> &'static Type {
 | |
|     match s {
 | |
|         "float16_t" => &F16,
 | |
|         "float16x4_t" => &F16X4,
 | |
|         "float16x4x2_t" => &F16X4X2,
 | |
|         "float16x4x3_t" => &F16X4X3,
 | |
|         "float16x4x4_t" => &F16X4X4,
 | |
|         "float16x8_t" => &F16X8,
 | |
|         "float16x8x2_t" => &F16X8X2,
 | |
|         "float16x8x3_t" => &F16X8X3,
 | |
|         "float16x8x4_t" => &F16X8X4,
 | |
|         "float32_t" => &F32,
 | |
|         "float32x2_t" => &F32X2,
 | |
|         "float32x2x2_t" => &F32X2X2,
 | |
|         "float32x2x3_t" => &F32X2X3,
 | |
|         "float32x2x4_t" => &F32X2X4,
 | |
|         "float32x4_t" => &F32X4,
 | |
|         "float32x4x2_t" => &F32X4X2,
 | |
|         "float32x4x3_t" => &F32X4X3,
 | |
|         "float32x4x4_t" => &F32X4X4,
 | |
|         "float64_t" => &F64,
 | |
|         "float64x1_t" => &F64X1,
 | |
|         "float64x1x2_t" => &F64X1X2,
 | |
|         "float64x1x3_t" => &F64X1X3,
 | |
|         "float64x1x4_t" => &F64X1X4,
 | |
|         "float64x2_t" => &F64X2,
 | |
|         "float64x2x2_t" => &F64X2X2,
 | |
|         "float64x2x3_t" => &F64X2X3,
 | |
|         "float64x2x4_t" => &F64X2X4,
 | |
|         "int16_t" => &I16,
 | |
|         "int16x2_t" => &I16X2,
 | |
|         "int16x4_t" => &I16X4,
 | |
|         "int16x4x2_t" => &I16X4X2,
 | |
|         "int16x4x3_t" => &I16X4X3,
 | |
|         "int16x4x4_t" => &I16X4X4,
 | |
|         "int16x8_t" => &I16X8,
 | |
|         "int16x8x2_t" => &I16X8X2,
 | |
|         "int16x8x3_t" => &I16X8X3,
 | |
|         "int16x8x4_t" => &I16X8X4,
 | |
|         "int32_t" | "int" => &I32,
 | |
|         "int32x2_t" => &I32X2,
 | |
|         "int32x2x2_t" => &I32X2X2,
 | |
|         "int32x2x3_t" => &I32X2X3,
 | |
|         "int32x2x4_t" => &I32X2X4,
 | |
|         "int32x4_t" => &I32X4,
 | |
|         "int32x4x2_t" => &I32X4X2,
 | |
|         "int32x4x3_t" => &I32X4X3,
 | |
|         "int32x4x4_t" => &I32X4X4,
 | |
|         "int64_t" => &I64,
 | |
|         "int64x1_t" => &I64X1,
 | |
|         "int64x1x2_t" => &I64X1X2,
 | |
|         "int64x1x3_t" => &I64X1X3,
 | |
|         "int64x1x4_t" => &I64X1X4,
 | |
|         "int64x2_t" => &I64X2,
 | |
|         "int64x2x2_t" => &I64X2X2,
 | |
|         "int64x2x3_t" => &I64X2X3,
 | |
|         "int64x2x4_t" => &I64X2X4,
 | |
|         "int8_t" => &I8,
 | |
|         "int8x16_t" => &I8X16,
 | |
|         "int8x16x2_t" => &I8X16X2,
 | |
|         "int8x16x3_t" => &I8X16X3,
 | |
|         "int8x16x4_t" => &I8X16X4,
 | |
|         "int8x4_t" => &I8X4,
 | |
|         "int8x8_t" => &I8X8,
 | |
|         "int8x8x2_t" => &I8X8X2,
 | |
|         "int8x8x3_t" => &I8X8X3,
 | |
|         "int8x8x4_t" => &I8X8X4,
 | |
|         "poly128_t" => &P128,
 | |
|         "poly16_t" => &P16,
 | |
|         "poly16x4_t" => &POLY16X4,
 | |
|         "poly16x4x2_t" => &P16X4X2,
 | |
|         "poly16x4x3_t" => &P16X4X3,
 | |
|         "poly16x4x4_t" => &P16X4X4,
 | |
|         "poly16x8_t" => &POLY16X8,
 | |
|         "poly16x8x2_t" => &P16X8X2,
 | |
|         "poly16x8x3_t" => &P16X8X3,
 | |
|         "poly16x8x4_t" => &P16X8X4,
 | |
|         "poly64_t" => &P64,
 | |
|         "poly64x1_t" => &POLY64X1,
 | |
|         "poly64x1x2_t" => &P64X1X2,
 | |
|         "poly64x1x3_t" => &P64X1X3,
 | |
|         "poly64x1x4_t" => &P64X1X4,
 | |
|         "poly64x2_t" => &POLY64X2,
 | |
|         "poly64x2x2_t" => &P64X2X2,
 | |
|         "poly64x2x3_t" => &P64X2X3,
 | |
|         "poly64x2x4_t" => &P64X2X4,
 | |
|         "poly8_t" => &P8,
 | |
|         "poly8x16_t" => &POLY8X16,
 | |
|         "poly8x16x2_t" => &POLY8X16X2,
 | |
|         "poly8x16x3_t" => &POLY8X16X3,
 | |
|         "poly8x16x4_t" => &POLY8X16X4,
 | |
|         "poly8x8_t" => &POLY8X8,
 | |
|         "poly8x8x2_t" => &POLY8X8X2,
 | |
|         "poly8x8x3_t" => &POLY8X8X3,
 | |
|         "poly8x8x4_t" => &POLY8X8X4,
 | |
|         "uint16_t" => &U16,
 | |
|         "uint16x4_t" => &U16X4,
 | |
|         "uint16x4x2_t" => &U16X4X2,
 | |
|         "uint16x4x3_t" => &U16X4X3,
 | |
|         "uint16x4x4_t" => &U16X4X4,
 | |
|         "uint16x8_t" => &U16X8,
 | |
|         "uint16x8x2_t" => &U16X8X2,
 | |
|         "uint16x8x3_t" => &U16X8X3,
 | |
|         "uint16x8x4_t" => &U16X8X4,
 | |
|         "uint32_t" => &U32,
 | |
|         "uint32x2_t" => &U32X2,
 | |
|         "uint32x2x2_t" => &U32X2X2,
 | |
|         "uint32x2x3_t" => &U32X2X3,
 | |
|         "uint32x2x4_t" => &U32X2X4,
 | |
|         "uint32x4_t" => &U32X4,
 | |
|         "uint32x4x2_t" => &U32X4X2,
 | |
|         "uint32x4x3_t" => &U32X4X3,
 | |
|         "uint32x4x4_t" => &U32X4X4,
 | |
|         "uint64_t" => &U64,
 | |
|         "uint64x1_t" => &U64X1,
 | |
|         "uint64x1x2_t" => &U64X1X2,
 | |
|         "uint64x1x3_t" => &U64X1X3,
 | |
|         "uint64x1x4_t" => &U64X1X4,
 | |
|         "uint64x2_t" => &U64X2,
 | |
|         "uint64x2x2_t" => &U64X2X2,
 | |
|         "uint64x2x3_t" => &U64X2X3,
 | |
|         "uint64x2x4_t" => &U64X2X4,
 | |
|         "uint8_t" => &U8,
 | |
|         "uint8x16_t" => &U8X16,
 | |
|         "uint8x16x2_t" => &U8X16X2,
 | |
|         "uint8x16x3_t" => &U8X16X3,
 | |
|         "uint8x16x4_t" => &U8X16X4,
 | |
|         "uint8x8_t" => &U8X8,
 | |
|         "uint8x8x2_t" => &U8X8X2,
 | |
|         "uint8x8x3_t" => &U8X8X3,
 | |
|         "uint8x8x4_t" => &U8X8X4,
 | |
| 
 | |
|         _ => panic!("failed to parse html type {:?}", s),
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn collect_text(s: &mut String, node: &Node) {
 | |
|     if let NodeData::Text { contents } = &node.data {
 | |
|         s.push(' ');
 | |
|         s.push_str(&contents.borrow().to_string());
 | |
|     }
 | |
|     for child in node.children.borrow().iter() {
 | |
|         collect_text(s, child);
 | |
|     }
 | |
| }
 | 
