1 //! Defines x64 instructions using the DSL.
2 
3 mod abs;
4 mod add;
5 mod align;
6 mod and;
7 mod atomic;
8 mod avg;
9 mod bitmanip;
10 mod cmov;
11 mod cmp;
12 mod cvt;
13 mod div;
14 mod fma;
15 mod jmp;
16 mod lanes;
17 mod max;
18 mod min;
19 mod misc;
20 mod mov;
21 mod mul;
22 mod neg;
23 mod nop;
24 mod or;
25 mod pack;
26 mod pma;
27 mod recip;
28 mod round;
29 mod setcc;
30 mod shift;
31 mod sqrt;
32 mod stack;
33 mod sub;
34 mod unpack;
35 mod xor;
36 
37 use crate::dsl::{Feature, Inst, Mutability, OperandKind};
38 use std::collections::HashMap;
39 
40 #[must_use]
list() -> Vec<Inst>41 pub fn list() -> Vec<Inst> {
42     let mut all = vec![];
43     all.extend(abs::list());
44     all.extend(add::list());
45     all.extend(align::list());
46     all.extend(and::list());
47     all.extend(atomic::list());
48     all.extend(avg::list());
49     all.extend(bitmanip::list());
50     all.extend(cmov::list());
51     all.extend(cmp::list());
52     all.extend(cvt::list());
53     all.extend(div::list());
54     all.extend(fma::list());
55     all.extend(jmp::list());
56     all.extend(lanes::list());
57     all.extend(max::list());
58     all.extend(min::list());
59     all.extend(misc::list());
60     all.extend(mov::list());
61     all.extend(mul::list());
62     all.extend(neg::list());
63     all.extend(nop::list());
64     all.extend(or::list());
65     all.extend(pack::list());
66     all.extend(pma::list());
67     all.extend(recip::list());
68     all.extend(round::list());
69     all.extend(setcc::list());
70     all.extend(shift::list());
71     all.extend(sqrt::list());
72     all.extend(stack::list());
73     all.extend(sub::list());
74     all.extend(unpack::list());
75     all.extend(xor::list());
76 
77     check_avx_alternates(&mut all);
78 
79     all
80 }
81 
82 /// Checks that assigned AVX alternates are correctly applied to SSE
83 /// instructions.
84 ///
85 /// # Panics
86 ///
87 /// Expects that each AVX alternate to be of an SSE instruction (currently).
check_avx_alternates(all: &mut [Inst])88 fn check_avx_alternates(all: &mut [Inst]) {
89     let name_to_index: HashMap<String, usize> = all
90         .iter()
91         .enumerate()
92         .map(|(index, inst)| (inst.name().clone(), index))
93         .collect();
94     for inst in all.iter().filter(|inst| inst.alternate.is_some()) {
95         assert!(
96             inst.features.is_sse(),
97             "expected an SSE instruction: {inst}"
98         );
99         let alternate = inst.alternate.as_ref().unwrap();
100         assert_eq!(alternate.feature, Feature::avx);
101         let avx_index = name_to_index.get(&alternate.name).expect(&format!(
102             "invalid alternate name: {} (did you use the full `<mnemonic>_<format>` form?)",
103             alternate.name
104         ));
105         check_sse_matches_avx(inst, &all[*avx_index]);
106     }
107 }
108 
109 /// Checks if the SSE instruction `sse_inst` matches the AVX instruction
110 /// `avx_inst` in terms of operands and opcode.
111 ///
112 /// # Panics
113 ///
114 /// Panics for any condition indicating that the SSE and AVX instructions do not
115 /// match:
116 /// - the AVX instruction does not have a 'v' prefix
117 /// - the SSE and AVX instructions do not have the same opcode
118 /// - the operand formats do not match the expected patterns
check_sse_matches_avx(sse_inst: &Inst, avx_inst: &Inst)119 fn check_sse_matches_avx(sse_inst: &Inst, avx_inst: &Inst) {
120     use crate::dsl::{Mutability::*, OperandKind::*};
121 
122     debug_assert_eq!(
123         &format!("v{}", sse_inst.mnemonic),
124         &avx_inst.mnemonic,
125         "an alternate AVX instruction should have a 'v' prefix: {avx_inst}"
126     );
127 
128     if sse_inst.encoding.opcode() != avx_inst.encoding.opcode() {
129         panic!("alternate instructions should have the same opcode:\n{sse_inst}\n{avx_inst}");
130     }
131 
132     match (list_ops(sse_inst).as_slice(), list_ops(avx_inst).as_slice()) {
133         // For now, we only really want to tie together SSE instructions that
134         // look like `rw(xmm), r(xmm_m*)` with their AVX counterpart that looks
135         // like `w(xmm), r(xmm), r(xmm_m*)`. This is because the relationship
136         // between these kinds of instructions is quite regular. Other formats
137         // may have slightly different operand semantics (e.g., `roundss` ->
138         // `vroundss`) and we want to be careful about matching too freely.
139         (
140             [
141                 (ReadWrite | Write, Reg(_)),
142                 (Read, Reg(_) | RegMem(_) | Mem(_)),
143             ],
144             [
145                 (Write, Reg(_)),
146                 (Read, Reg(_)),
147                 (Read, Reg(_) | RegMem(_) | Mem(_)),
148             ],
149         ) => {}
150         (
151             [(ReadWrite, Reg(_)), (Read, RegMem(_)), (Read, Imm(_))],
152             [
153                 (Write, Reg(_)),
154                 (Read, Reg(_)),
155                 (Read, RegMem(_)),
156                 (Read, Imm(_)),
157             ],
158         ) => {}
159         (
160             [(ReadWrite, Reg(_)), (Read, Imm(_))],
161             [(Write, Reg(_)), (Read, Reg(_)), (Read, Imm(_))],
162         ) => {}
163         // The following formats are identical.
164         (
165             [
166                 (Write, Reg(_) | RegMem(_) | Mem(_)),
167                 (Read, Reg(_) | RegMem(_) | Mem(_)),
168             ],
169             [
170                 (Write, Reg(_) | RegMem(_) | Mem(_)),
171                 (Read, Reg(_) | RegMem(_) | Mem(_)),
172             ],
173         ) => {}
174         (
175             [
176                 (Write, Reg(_) | RegMem(_)),
177                 (Read, Reg(_) | RegMem(_)),
178                 (Read, Imm(_)),
179             ],
180             [
181                 (Write, Reg(_) | RegMem(_)),
182                 (Read, Reg(_) | RegMem(_)),
183                 (Read, Imm(_)),
184             ],
185         ) => {}
186         ([(Read, Reg(_)), (Read, RegMem(_))], [(Read, Reg(_)), (Read, RegMem(_))]) => {}
187         // We panic on other formats for now; feel free to add more patterns to
188         // avoid this.
189         _ => panic!(
190             "unmatched formats for SSE-to-AVX alternate:\n{sse_inst}\n{avx_inst}. {:?}, {:?}",
191             list_ops(sse_inst),
192             list_ops(avx_inst)
193         ),
194     }
195 }
196 
197 /// Collect the mutability and kind of each operand in an instruction.
list_ops(inst: &Inst) -> Vec<(Mutability, OperandKind)>198 fn list_ops(inst: &Inst) -> Vec<(Mutability, OperandKind)> {
199     inst.format
200         .operands
201         .iter()
202         .map(|o| (o.mutability, o.location.kind()))
203         .collect()
204 }
205