cranelift_codegen/isa/x64/inst/
external.rs

1//! Interface with the external assembler crate.
2
3use super::{
4    regs, Amode, Gpr, Inst, LabelUse, MachBuffer, MachLabel, OperandVisitor, OperandVisitorImpl,
5    SyntheticAmode, VCodeConstant, WritableGpr, WritableXmm, Xmm,
6};
7use crate::ir::TrapCode;
8use cranelift_assembler_x64 as asm;
9
10/// Define the types of registers Cranelift will use.
11#[derive(Clone, Debug)]
12pub struct CraneliftRegisters;
13impl asm::Registers for CraneliftRegisters {
14    type ReadGpr = Gpr;
15    type ReadWriteGpr = PairedGpr;
16    type ReadXmm = Xmm;
17    type ReadWriteXmm = PairedXmm;
18}
19
20/// A pair of registers, one for reading and one for writing.
21///
22/// Due to how Cranelift's SSA form, we must track the read and write registers
23/// separately prior to register allocation. Once register allocation is
24/// complete, we expect the hardware encoding for both `read` and `write` to be
25/// the same.
26#[derive(Clone, Copy, Debug)]
27pub struct PairedGpr {
28    pub(crate) read: Gpr,
29    pub(crate) write: WritableGpr,
30}
31
32impl asm::AsReg for PairedGpr {
33    fn enc(&self) -> u8 {
34        let PairedGpr { read, write } = self;
35        let read = enc_gpr(read);
36        let write = enc_gpr(&write.to_reg());
37        assert_eq!(read, write);
38        write
39    }
40
41    fn new(_: u8) -> Self {
42        panic!("disallow creation of new assembler registers")
43    }
44}
45
46/// A pair of XMM registers, one for reading and one for writing.
47#[derive(Clone, Copy, Debug)]
48pub struct PairedXmm {
49    pub(crate) read: Xmm,
50    pub(crate) write: WritableXmm,
51}
52
53impl asm::AsReg for PairedXmm {
54    fn enc(&self) -> u8 {
55        let PairedXmm { read, write } = self;
56        let read = enc_xmm(read);
57        let write = enc_xmm(&write.to_reg());
58        assert_eq!(read, write);
59        write
60    }
61
62    fn new(_: u8) -> Self {
63        panic!("disallow creation of new assembler registers")
64    }
65}
66
67/// This bridges the gap between codegen and assembler for general purpose register types.
68impl asm::AsReg for Gpr {
69    fn enc(&self) -> u8 {
70        enc_gpr(self)
71    }
72
73    fn new(_: u8) -> Self {
74        panic!("disallow creation of new assembler registers")
75    }
76}
77
78/// This bridges the gap between codegen and assembler for xmm register types.
79impl asm::AsReg for Xmm {
80    fn enc(&self) -> u8 {
81        enc_xmm(self)
82    }
83
84    fn new(_: u8) -> Self {
85        panic!("disallow creation of new assembler registers")
86    }
87}
88
89/// A helper method for extracting the hardware encoding of a general purpose register.
90#[inline]
91fn enc_gpr(gpr: &Gpr) -> u8 {
92    if let Some(real) = gpr.to_reg().to_real_reg() {
93        real.hw_enc()
94    } else {
95        unreachable!()
96    }
97}
98
99/// A helper method for extracting the hardware encoding of an xmm register.
100#[inline]
101fn enc_xmm(xmm: &Xmm) -> u8 {
102    if let Some(real) = xmm.to_reg().to_real_reg() {
103        real.hw_enc()
104    } else {
105        unreachable!()
106    }
107}
108
109/// A wrapper to implement the `cranelift-assembler-x64` register allocation trait,
110/// `RegallocVisitor`, in terms of the trait used in Cranelift,
111/// `OperandVisitor`.
112pub(crate) struct RegallocVisitor<'a, T>
113where
114    T: OperandVisitorImpl,
115{
116    pub collector: &'a mut T,
117}
118
119impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for RegallocVisitor<'a, T> {
120    fn read(&mut self, reg: &mut Gpr) {
121        self.collector.reg_use(reg);
122    }
123
124    fn read_write(&mut self, reg: &mut PairedGpr) {
125        let PairedGpr { read, write } = reg;
126        self.collector.reg_use(read);
127        self.collector.reg_reuse_def(write, 0);
128    }
129
130    fn fixed_read(&mut self, _reg: &Gpr) {
131        todo!()
132    }
133
134    fn fixed_read_write(&mut self, _reg: &PairedGpr) {
135        todo!()
136    }
137
138    fn read_xmm(&mut self, reg: &mut Xmm) {
139        self.collector.reg_use(reg);
140    }
141
142    fn read_write_xmm(&mut self, reg: &mut PairedXmm) {
143        let PairedXmm { read, write } = reg;
144        self.collector.reg_use(read);
145        self.collector.reg_reuse_def(write, 0);
146    }
147
148    fn fixed_read_xmm(&mut self, _reg: &Xmm) {
149        todo!()
150    }
151
152    fn fixed_read_write_xmm(&mut self, _reg: &PairedXmm) {
153        todo!()
154    }
155}
156
157impl Into<asm::Amode<Gpr>> for SyntheticAmode {
158    fn into(self) -> asm::Amode<Gpr> {
159        match self {
160            SyntheticAmode::Real(amode) => match amode {
161                Amode::ImmReg {
162                    simm32,
163                    base,
164                    flags,
165                } => asm::Amode::ImmReg {
166                    simm32: asm::AmodeOffsetPlusKnownOffset {
167                        simm32: simm32.into(),
168                        offset: None,
169                    },
170                    base: Gpr::unwrap_new(base),
171                    trap: flags.trap_code().map(Into::into),
172                },
173                Amode::ImmRegRegShift {
174                    simm32,
175                    base,
176                    index,
177                    shift,
178                    flags,
179                } => asm::Amode::ImmRegRegShift {
180                    base,
181                    index: asm::NonRspGpr::new(index),
182                    scale: asm::Scale::new(shift),
183                    simm32: simm32.into(),
184                    trap: flags.trap_code().map(Into::into),
185                },
186                Amode::RipRelative { target } => asm::Amode::RipRelative {
187                    target: asm::DeferredTarget::Label(asm::Label(target.as_u32())),
188                },
189            },
190            SyntheticAmode::IncomingArg { offset } => asm::Amode::ImmReg {
191                base: Gpr::unwrap_new(regs::rbp()),
192                simm32: asm::AmodeOffsetPlusKnownOffset {
193                    simm32: (-i32::try_from(offset).unwrap()).into(),
194                    offset: Some(offsets::KEY_INCOMING_ARG),
195                },
196                trap: None,
197            },
198            SyntheticAmode::SlotOffset { simm32 } => asm::Amode::ImmReg {
199                base: Gpr::unwrap_new(regs::rbp()),
200                simm32: asm::AmodeOffsetPlusKnownOffset {
201                    simm32: simm32.into(),
202                    offset: Some(offsets::KEY_SLOT_OFFSET),
203                },
204                trap: None,
205            },
206            SyntheticAmode::ConstantOffset(vcode_constant) => asm::Amode::RipRelative {
207                target: asm::DeferredTarget::Constant(asm::Constant(vcode_constant.as_u32())),
208            },
209        }
210    }
211}
212
213/// Keep track of the offset slots to fill in during emission; see
214/// `KnownOffsetTable`.
215pub mod offsets {
216    pub const KEY_INCOMING_ARG: usize = 0;
217    pub const KEY_SLOT_OFFSET: usize = 1;
218}
219
220impl asm::CodeSink for MachBuffer<Inst> {
221    fn put1(&mut self, value: u8) {
222        self.put1(value)
223    }
224
225    fn put2(&mut self, value: u16) {
226        self.put2(value)
227    }
228
229    fn put4(&mut self, value: u32) {
230        self.put4(value)
231    }
232
233    fn put8(&mut self, value: u64) {
234        self.put8(value)
235    }
236
237    fn current_offset(&self) -> u32 {
238        self.cur_offset()
239    }
240
241    fn use_label_at_offset(&mut self, offset: u32, label: asm::Label) {
242        self.use_label_at_offset(offset, label.into(), LabelUse::JmpRel32);
243    }
244
245    fn add_trap(&mut self, code: asm::TrapCode) {
246        self.add_trap(code.into());
247    }
248
249    fn get_label_for_constant(&mut self, c: asm::Constant) -> asm::Label {
250        self.get_label_for_constant(c.into()).into()
251    }
252}
253
254impl From<asm::TrapCode> for TrapCode {
255    fn from(value: asm::TrapCode) -> Self {
256        Self::from_raw(value.0)
257    }
258}
259
260impl From<TrapCode> for asm::TrapCode {
261    fn from(value: TrapCode) -> Self {
262        Self(value.as_raw())
263    }
264}
265
266impl From<asm::Label> for MachLabel {
267    fn from(value: asm::Label) -> Self {
268        Self::from_u32(value.0)
269    }
270}
271
272impl From<MachLabel> for asm::Label {
273    fn from(value: MachLabel) -> Self {
274        Self(value.as_u32())
275    }
276}
277
278impl From<asm::Constant> for VCodeConstant {
279    fn from(value: asm::Constant) -> Self {
280        Self::from_u32(value.0)
281    }
282}
283
284// Include code generated by `cranelift-codegen/meta/src/gen_asm.rs`. This file
285// contains a `isle_assembler_methods!` macro with Rust implementations of all
286// the assembler instructions exposed to ISLE.
287include!(concat!(env!("OUT_DIR"), "/assembler-isle-macro.rs"));
288pub(crate) use isle_assembler_methods;