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,
6};
7use crate::ir::TrapCode;
8use arbitrary::Arbitrary;
9use cranelift_assembler_x64 as asm;
10
11/// Define the types of registers Cranelift will use.
12#[derive(Clone, Debug)]
13pub struct CraneliftRegisters;
14impl asm::Registers for CraneliftRegisters {
15    type ReadGpr = Gpr;
16    type ReadWriteGpr = PairedGpr;
17}
18
19/// A pair of registers, one for reading and one for writing.
20///
21/// Due to how Cranelift's SSA form, we must track the read and write registers
22/// separately prior to register allocation. Once register allocation is
23/// complete, we expect the hardware encoding for both `read` and `write` to be
24/// the same.
25#[derive(Clone, Copy, Debug)]
26pub struct PairedGpr {
27    pub(crate) read: Gpr,
28    pub(crate) write: WritableGpr,
29}
30
31impl asm::AsReg for PairedGpr {
32    fn enc(&self) -> u8 {
33        let PairedGpr { read, write } = self;
34        let read = enc(read);
35        let write = enc(&write.to_reg());
36        assert_eq!(read, write);
37        write
38    }
39
40    fn new(_: u8) -> Self {
41        panic!("disallow creation of new assembler registers")
42    }
43}
44
45impl<'a> Arbitrary<'a> for PairedGpr {
46    fn arbitrary(_: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
47        unimplemented!("assembler fuzzing is not implemented at this level")
48    }
49}
50
51/// This bridges the gap between codegen and assembler register types.
52impl asm::AsReg for Gpr {
53    fn enc(&self) -> u8 {
54        enc(self)
55    }
56
57    fn new(_: u8) -> Self {
58        panic!("disallow creation of new assembler registers")
59    }
60}
61
62impl<'a> Arbitrary<'a> for Gpr {
63    fn arbitrary(_: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
64        unimplemented!("assembler fuzzing is not implemented at this level")
65    }
66}
67
68/// A helper method for extracting the hardware encoding of a register.
69#[inline]
70fn enc(gpr: &Gpr) -> u8 {
71    if let Some(real) = gpr.to_reg().to_real_reg() {
72        real.hw_enc()
73    } else {
74        unreachable!()
75    }
76}
77
78/// A wrapper to implement the `cranelift-assembler-x64` register allocation trait,
79/// `RegallocVisitor`, in terms of the trait used in Cranelift,
80/// `OperandVisitor`.
81pub(crate) struct RegallocVisitor<'a, T>
82where
83    T: OperandVisitorImpl,
84{
85    pub collector: &'a mut T,
86}
87
88impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for RegallocVisitor<'a, T> {
89    fn read(&mut self, reg: &mut Gpr) {
90        self.collector.reg_use(reg);
91    }
92
93    fn read_write(&mut self, reg: &mut PairedGpr) {
94        let PairedGpr { read, write } = reg;
95        self.collector.reg_use(read);
96        self.collector.reg_reuse_def(write, 0);
97    }
98
99    fn fixed_read(&mut self, _reg: &Gpr) {
100        todo!()
101    }
102
103    fn fixed_read_write(&mut self, _reg: &PairedGpr) {
104        todo!()
105    }
106}
107
108impl Into<asm::Amode<Gpr>> for SyntheticAmode {
109    fn into(self) -> asm::Amode<Gpr> {
110        match self {
111            SyntheticAmode::Real(amode) => match amode {
112                Amode::ImmReg {
113                    simm32,
114                    base,
115                    flags,
116                } => asm::Amode::ImmReg {
117                    simm32: asm::Simm32PlusKnownOffset {
118                        simm32: simm32.into(),
119                        offset: None,
120                    },
121                    base: Gpr::unwrap_new(base),
122                    trap: flags.trap_code().map(Into::into),
123                },
124                Amode::ImmRegRegShift {
125                    simm32,
126                    base,
127                    index,
128                    shift,
129                    flags,
130                } => asm::Amode::ImmRegRegShift {
131                    base,
132                    index: asm::NonRspGpr::new(index),
133                    scale: asm::Scale::new(shift),
134                    simm32: simm32.into(),
135                    trap: flags.trap_code().map(Into::into),
136                },
137                Amode::RipRelative { target } => asm::Amode::RipRelative {
138                    target: asm::DeferredTarget::Label(asm::Label(target.as_u32())),
139                },
140            },
141            SyntheticAmode::IncomingArg { offset } => asm::Amode::ImmReg {
142                base: Gpr::unwrap_new(regs::rbp()),
143                simm32: asm::Simm32PlusKnownOffset {
144                    simm32: (-i32::try_from(offset).unwrap()).into(),
145                    offset: Some(offsets::KEY_INCOMING_ARG),
146                },
147                trap: None,
148            },
149            SyntheticAmode::SlotOffset { simm32 } => asm::Amode::ImmReg {
150                base: Gpr::unwrap_new(regs::rbp()),
151                simm32: asm::Simm32PlusKnownOffset {
152                    simm32: simm32.into(),
153                    offset: Some(offsets::KEY_SLOT_OFFSET),
154                },
155                trap: None,
156            },
157            SyntheticAmode::ConstantOffset(vcode_constant) => asm::Amode::RipRelative {
158                target: asm::DeferredTarget::Constant(asm::Constant(vcode_constant.as_u32())),
159            },
160        }
161    }
162}
163
164/// Keep track of the offset slots to fill in during emission; see
165/// `KnownOffsetTable`.
166pub mod offsets {
167    pub const KEY_INCOMING_ARG: usize = 0;
168    pub const KEY_SLOT_OFFSET: usize = 1;
169}
170
171impl asm::CodeSink for MachBuffer<Inst> {
172    fn put1(&mut self, value: u8) {
173        self.put1(value)
174    }
175
176    fn put2(&mut self, value: u16) {
177        self.put2(value)
178    }
179
180    fn put4(&mut self, value: u32) {
181        self.put4(value)
182    }
183
184    fn put8(&mut self, value: u64) {
185        self.put8(value)
186    }
187
188    fn current_offset(&self) -> u32 {
189        self.cur_offset()
190    }
191
192    fn use_label_at_offset(&mut self, offset: u32, label: asm::Label) {
193        self.use_label_at_offset(offset, label.into(), LabelUse::JmpRel32);
194    }
195
196    fn add_trap(&mut self, code: asm::TrapCode) {
197        self.add_trap(code.into());
198    }
199
200    fn get_label_for_constant(&mut self, c: asm::Constant) -> asm::Label {
201        self.get_label_for_constant(c.into()).into()
202    }
203}
204
205impl From<asm::TrapCode> for TrapCode {
206    fn from(value: asm::TrapCode) -> Self {
207        Self::from_raw(value.0)
208    }
209}
210
211impl From<TrapCode> for asm::TrapCode {
212    fn from(value: TrapCode) -> Self {
213        Self(value.as_raw())
214    }
215}
216
217impl From<asm::Label> for MachLabel {
218    fn from(value: asm::Label) -> Self {
219        Self::from_u32(value.0)
220    }
221}
222
223impl From<MachLabel> for asm::Label {
224    fn from(value: MachLabel) -> Self {
225        Self(value.as_u32())
226    }
227}
228
229impl From<asm::Constant> for VCodeConstant {
230    fn from(value: asm::Constant) -> Self {
231        Self::from_u32(value.0)
232    }
233}