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