cranelift_assembler_x64_meta/
instructions.rs

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