1pub mod instructions;
2
3pub use instructions::{
4 parse_instructions_toml, validate_instructions_table, validate_stack_entry, ControlFrame,
5 ControlKind, Immediate, Instruction, InstructionsTable, LabelTarget, Opcode, StackEntry,
6 StackType, TypeExpr, ValidateError,
7};
8
9#[cfg(feature = "instructions-toml")]
10pub use instructions::{instructions, INSTRUCTIONS_TOML};
11
12#[cfg(test)]
13mod tests {
14 use super::*;
15
16 const RELAXED_SIMD_NAMES: &[&str] = &[
17 "i8x16.relaxed_swizzle",
18 "i32x4.relaxed_trunc_f32x4_s",
19 "i32x4.relaxed_trunc_f32x4_u",
20 "i32x4.relaxed_trunc_f64x2_s_zero",
21 "i32x4.relaxed_trunc_f64x2_u_zero",
22 "f32x4.relaxed_madd",
23 "f32x4.relaxed_nmadd",
24 "f64x2.relaxed_madd",
25 "f64x2.relaxed_nmadd",
26 "i8x16.relaxed_laneselect",
27 "i16x8.relaxed_laneselect",
28 "i32x4.relaxed_laneselect",
29 "i64x2.relaxed_laneselect",
30 "f32x4.relaxed_min",
31 "f32x4.relaxed_max",
32 "f64x2.relaxed_min",
33 "f64x2.relaxed_max",
34 "i16x8.relaxed_q15mulr_s",
35 "i16x8.relaxed_dot_i8x16_i7x16_s",
36 "i32x4.relaxed_dot_i8x16_i7x16_add_s",
37 ];
38
39 const SAMPLE: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/instructions.toml"));
40
41 #[test]
42 fn table_parses() {
43 let table = parse_instructions_toml(SAMPLE).unwrap();
44 assert!(table.instructions.len() >= 566);
45 assert_eq!(table.instructions[0].name, "unreachable");
46 assert_eq!(table.instructions[0].opcode, Opcode::Single(0));
47 validate_instructions_table(&table).unwrap();
48 }
49
50 #[test]
51 fn multi_byte_opcode_and_immediates() {
52 let table = parse_instructions_toml(SAMPLE).unwrap();
53 let fill = table
54 .instructions
55 .iter()
56 .find(|i| i.name == "table.fill")
57 .expect("table.fill");
58 assert_eq!(fill.opcode, Opcode::Multi(0xFC, 17));
59 let imms = fill.immediates.as_ref().expect("immediates");
60 assert_eq!(imms[0].ty, "tableidx");
61 assert_eq!(imms[0].name.as_deref(), Some("x"));
62 }
63
64 #[test]
65 fn control_stack_entry() {
66 let table = parse_instructions_toml(SAMPLE).unwrap();
67 let block = table
68 .instructions
69 .iter()
70 .find(|i| i.name == "block")
71 .expect("block");
72 let stack = block.stack_type.as_ref().expect("stack-type");
73 match &stack.to[0] {
74 StackEntry::Control(cf) => {
75 assert_eq!(cf.control, ControlKind::Block);
76 assert_eq!(cf.start.0, "params(bt)");
77 assert_eq!(cf.end.0, "results(bt)");
78 assert_eq!(cf.label, LabelTarget::End);
79 }
80 _ => panic!("expected control frame"),
81 }
82 }
83
84 #[test]
85 fn types_of_vs_type_of() {
86 let table = parse_instructions_toml(SAMPLE).unwrap();
87
88 let block = table
89 .instructions
90 .iter()
91 .find(|i| i.name == "block")
92 .expect("block");
93 match &block.stack_type.as_ref().unwrap().from[0] {
94 StackEntry::TypesOf(expr) => assert_eq!(expr.0, "params(bt)"),
95 _ => panic!("expected types-of"),
96 }
97
98 let local_get = table
99 .instructions
100 .iter()
101 .find(|i| i.name == "local.get")
102 .expect("local.get");
103 match &local_get.stack_type.as_ref().unwrap().to[0] {
104 StackEntry::TypeOf(expr) => assert_eq!(expr.0, "x"),
105 _ => panic!("expected type-of"),
106 }
107 }
108
109 #[test]
110 fn relaxed_simd_instructions() {
111 let table = parse_instructions_toml(SAMPLE).unwrap();
112 let relaxed: Vec<_> = table
113 .instructions
114 .iter()
115 .filter(|i| i.feature.as_deref() == Some("relaxed-simd"))
116 .collect();
117 assert_eq!(relaxed.len(), 20);
118
119 for (idx, name) in RELAXED_SIMD_NAMES.iter().enumerate() {
120 let insn = table
121 .instructions
122 .iter()
123 .find(|i| i.name == *name)
124 .unwrap_or_else(|| panic!("missing {name}"));
125 assert_eq!(insn.opcode, Opcode::Multi(0xFD, idx as u32 + 256));
126 assert_eq!(insn.category, "vector");
127 assert_eq!(insn.feature.as_deref(), Some("relaxed-simd"));
128 }
129 }
130
131 #[test]
132 fn loop_label_is_start() {
133 let table = parse_instructions_toml(SAMPLE).unwrap();
134 let loop_insn = table
135 .instructions
136 .iter()
137 .find(|i| i.name == "loop")
138 .expect("loop");
139 match &loop_insn.stack_type.as_ref().unwrap().to[0] {
140 StackEntry::Control(cf) => assert_eq!(cf.label, LabelTarget::Start),
141 _ => panic!("expected control frame"),
142 }
143 }
144}
145
146#[cfg(all(test, feature = "instructions-toml"))]
147mod embedded_tests {
148 use super::*;
149
150 #[test]
151 fn embedded_entrypoint() {
152 let table = instructions();
153 assert!(table.instructions.len() >= 566);
154 assert!(!INSTRUCTIONS_TOML.is_empty());
155 validate_instructions_table(table).unwrap();
156 }
157}