cranelift_assembler_x64_meta/
instructions.rs

1//! Defines x64 instructions using the DSL.
2
3mod abs;
4mod add;
5mod align;
6mod and;
7mod atomic;
8mod avg;
9mod bitmanip;
10mod cmov;
11mod cmp;
12mod cvt;
13mod div;
14mod fma;
15mod jmp;
16mod lanes;
17mod max;
18mod min;
19mod misc;
20mod mov;
21mod mul;
22mod neg;
23mod nop;
24mod or;
25mod pack;
26mod pma;
27mod recip;
28mod round;
29mod setcc;
30mod shift;
31mod sqrt;
32mod stack;
33mod sub;
34mod unpack;
35mod xor;
36
37use crate::dsl::{Feature, Inst, Mutability, OperandKind};
38use std::collections::HashMap;
39
40#[must_use]
41pub 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).
88fn 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
119fn 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.
198fn 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}