Coverage Report

Created: 2026-03-26 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cranelift-assembler-x64-0.129.1/src/mem.rs
Line
Count
Source
1
//! Memory operands to instructions.
2
3
use alloc::string::{String, ToString};
4
5
use crate::api::{AsReg, CodeSink, Constant, KnownOffset, Label, TrapCode};
6
use crate::gpr::{self, NonRspGpr, Size};
7
use crate::rex::{Disp, RexPrefix, encode_modrm, encode_sib};
8
9
/// x64 memory addressing modes.
10
#[derive(Copy, Clone, Debug, PartialEq)]
11
#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
12
pub enum Amode<R: AsReg> {
13
    ImmReg {
14
        base: R,
15
        simm32: AmodeOffsetPlusKnownOffset,
16
        trap: Option<TrapCode>,
17
    },
18
    ImmRegRegShift {
19
        base: R,
20
        index: NonRspGpr<R>,
21
        scale: Scale,
22
        simm32: AmodeOffset,
23
        trap: Option<TrapCode>,
24
    },
25
    RipRelative {
26
        target: DeferredTarget,
27
    },
28
}
29
30
impl<R: AsReg> Amode<R> {
31
    /// Return the [`TrapCode`] associated with this [`Amode`], if any.
32
8.70M
    pub fn trap_code(&self) -> Option<TrapCode> {
33
8.70M
        match self {
34
8.63M
            Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap,
35
70.9k
            Amode::RipRelative { .. } => None,
36
        }
37
8.70M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::trap_code
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<_>>::trap_code
<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::trap_code
Line
Count
Source
32
8.70M
    pub fn trap_code(&self) -> Option<TrapCode> {
33
8.70M
        match self {
34
8.63M
            Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap,
35
70.9k
            Amode::RipRelative { .. } => None,
36
        }
37
8.70M
    }
38
39
    /// Return the [`RexPrefix`] for each variant of this [`Amode`].
40
    #[must_use]
41
7.68M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
42
7.68M
        match self {
43
7.56M
            Amode::ImmReg { base, .. } => {
44
7.56M
                RexPrefix::mem_op(enc_reg, base.enc(), has_w_bit, uses_8bit)
45
            }
46
96.6k
            Amode::ImmRegRegShift { base, index, .. } => {
47
96.6k
                RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit)
48
            }
49
25.4k
            Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit),
50
        }
51
7.68M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<_>>::as_rex_prefix
<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
41
7.68M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
42
7.68M
        match self {
43
7.56M
            Amode::ImmReg { base, .. } => {
44
7.56M
                RexPrefix::mem_op(enc_reg, base.enc(), has_w_bit, uses_8bit)
45
            }
46
96.6k
            Amode::ImmRegRegShift { base, index, .. } => {
47
96.6k
                RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit)
48
            }
49
25.4k
            Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit),
50
        }
51
7.68M
    }
52
53
    /// Emit the ModR/M, SIB, and displacement suffixes as needed for this
54
    /// `Amode`.
55
8.70M
    pub(crate) fn encode_rex_suffixes(
56
8.70M
        &self,
57
8.70M
        sink: &mut impl CodeSink,
58
8.70M
        enc_reg: u8,
59
8.70M
        bytes_at_end: u8,
60
8.70M
        evex_scaling: Option<i8>,
61
8.70M
    ) {
62
8.70M
        emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling);
63
8.70M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<_>>::encode_rex_suffixes::<_>
<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
55
8.70M
    pub(crate) fn encode_rex_suffixes(
56
8.70M
        &self,
57
8.70M
        sink: &mut impl CodeSink,
58
8.70M
        enc_reg: u8,
59
8.70M
        bytes_at_end: u8,
60
8.70M
        evex_scaling: Option<i8>,
61
8.70M
    ) {
62
8.70M
        emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling);
63
8.70M
    }
64
65
    /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX
66
    /// prefix).
67
    ///
68
    /// During encoding, the `b` bit is set by the topmost bit (the fourth bit)
69
    /// of either the `reg` register or, if this is a memory address, the `base`
70
    /// register. The `x` bit is set by the `index` register, when used.
71
1.02M
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
72
1.02M
        match self {
73
972k
            Amode::ImmReg { base, .. } => (Some(base.enc()), None),
74
4.29k
            Amode::ImmRegRegShift { base, index, .. } => (Some(base.enc()), Some(index.enc())),
75
45.5k
            Amode::RipRelative { .. } => (None, None),
76
        }
77
1.02M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<_>>::encode_bx_regs
<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Line
Count
Source
71
1.02M
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
72
1.02M
        match self {
73
972k
            Amode::ImmReg { base, .. } => (Some(base.enc()), None),
74
4.29k
            Amode::ImmRegRegShift { base, index, .. } => (Some(base.enc()), Some(index.enc())),
75
45.5k
            Amode::RipRelative { .. } => (None, None),
76
        }
77
1.02M
    }
78
}
79
80
/// A 32-bit immediate for address offsets.
81
#[derive(Clone, Copy, Debug, PartialEq)]
82
pub struct AmodeOffset(i32);
83
84
impl AmodeOffset {
85
    pub const ZERO: AmodeOffset = AmodeOffset::new(0);
86
87
    #[must_use]
88
0
    pub const fn new(value: i32) -> Self {
89
0
        Self(value)
90
0
    }
91
92
    #[must_use]
93
8.64M
    pub fn value(self) -> i32 {
94
8.64M
        self.0
95
8.64M
    }
96
}
97
98
impl From<i32> for AmodeOffset {
99
8.64M
    fn from(value: i32) -> Self {
100
8.64M
        Self(value)
101
8.64M
    }
102
}
103
104
impl core::fmt::LowerHex for AmodeOffset {
105
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
106
        // This rather complex implementation is necessary to match how
107
        // `capstone` pretty-prints memory immediates.
108
0
        if self.0 == 0 {
109
0
            return Ok(());
110
0
        }
111
0
        if self.0 < 0 {
112
0
            write!(f, "-")?;
113
0
        }
114
0
        if self.0 > 9 || self.0 < -9 {
115
0
            write!(f, "0x")?;
116
0
        }
117
0
        let abs = match self.0.checked_abs() {
118
0
            Some(i) => i,
119
0
            None => -2_147_483_648,
120
        };
121
0
        core::fmt::LowerHex::fmt(&abs, f)
122
0
    }
123
}
124
125
/// An [`AmodeOffset`] immediate with an optional known offset.
126
///
127
/// Cranelift does not know certain offsets until emission time. To accommodate
128
/// Cranelift, this structure stores an optional [`KnownOffset`]. The following
129
/// happens immediately before emission:
130
/// - the [`KnownOffset`] is looked up, mapping it to an offset value
131
/// - the [`AmodeOffset`] value is added to the offset value
132
#[derive(Copy, Clone, Debug, PartialEq)]
133
pub struct AmodeOffsetPlusKnownOffset {
134
    pub simm32: AmodeOffset,
135
    pub offset: Option<KnownOffset>,
136
}
137
138
impl AmodeOffsetPlusKnownOffset {
139
    pub const ZERO: AmodeOffsetPlusKnownOffset = AmodeOffsetPlusKnownOffset {
140
        simm32: AmodeOffset::ZERO,
141
        offset: None,
142
    };
143
144
    /// # Panics
145
    ///
146
    /// Panics if the sum of the immediate and the known offset value overflows.
147
    #[must_use]
148
8.53M
    pub fn value(&self, sink: &impl CodeSink) -> i32 {
149
8.53M
        let known_offset = match self.offset {
150
1.93M
            Some(offset) => sink.known_offset(offset),
151
6.60M
            None => 0,
152
        };
153
8.53M
        known_offset
154
8.53M
            .checked_add(self.simm32.value())
155
8.53M
            .expect("no wrapping")
156
8.53M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::AmodeOffsetPlusKnownOffset>::value::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::AmodeOffsetPlusKnownOffset>::value::<_>
<cranelift_assembler_x64::mem::AmodeOffsetPlusKnownOffset>::value::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
148
8.53M
    pub fn value(&self, sink: &impl CodeSink) -> i32 {
149
8.53M
        let known_offset = match self.offset {
150
1.93M
            Some(offset) => sink.known_offset(offset),
151
6.60M
            None => 0,
152
        };
153
8.53M
        known_offset
154
8.53M
            .checked_add(self.simm32.value())
155
8.53M
            .expect("no wrapping")
156
8.53M
    }
157
}
158
159
impl core::fmt::LowerHex for AmodeOffsetPlusKnownOffset {
160
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
161
0
        if let Some(offset) = self.offset {
162
0
            write!(f, "<offset:{offset}>+")?;
163
0
        }
164
0
        core::fmt::LowerHex::fmt(&self.simm32, f)
165
0
    }
166
}
167
168
/// For RIP-relative addressing, keep track of the [`CodeSink`]-specific target.
169
#[derive(Copy, Clone, Debug, PartialEq)]
170
#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
171
pub enum DeferredTarget {
172
    Label(Label),
173
    Constant(Constant),
174
    None,
175
}
176
177
impl<R: AsReg> core::fmt::Display for Amode<R> {
178
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179
0
        let pointer_width = Size::Quadword;
180
0
        match self {
181
0
            Amode::ImmReg { simm32, base, .. } => {
182
                // Note: size is always 8; the address is 64 bits,
183
                // even if the addressed operand is smaller.
184
0
                let base = base.to_string(Some(pointer_width));
185
0
                write!(f, "{simm32:x}({base})")
186
            }
187
            Amode::ImmRegRegShift {
188
0
                simm32,
189
0
                base,
190
0
                index,
191
0
                scale,
192
                ..
193
            } => {
194
0
                let base = base.to_string(Some(pointer_width));
195
0
                let index = index.to_string(pointer_width);
196
0
                let shift = scale.shift();
197
0
                if shift > 1 {
198
0
                    write!(f, "{simm32:x}({base}, {index}, {shift})")
199
                } else {
200
0
                    write!(f, "{simm32:x}({base}, {index})")
201
                }
202
            }
203
0
            Amode::RipRelative { .. } => write!(f, "(%rip)"),
204
        }
205
0
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr> as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<_> as core::fmt::Display>::fmt
Unexecuted instantiation: <cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr> as core::fmt::Display>::fmt
206
}
207
208
/// The scaling factor for the index register in certain [`Amode`]s.
209
#[derive(Copy, Clone, Debug, PartialEq)]
210
#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
211
pub enum Scale {
212
    One,
213
    Two,
214
    Four,
215
    Eight,
216
}
217
218
impl Scale {
219
    /// Create a new [`Scale`] from its hardware encoding.
220
    ///
221
    /// # Panics
222
    ///
223
    /// Panics if `enc` is not a valid encoding for a scale (0-3).
224
    #[must_use]
225
103k
    pub fn new(enc: u8) -> Self {
226
103k
        match enc {
227
103k
            0b00 => Scale::One,
228
6
            0b01 => Scale::Two,
229
307
            0b10 => Scale::Four,
230
290
            0b11 => Scale::Eight,
231
0
            _ => panic!("invalid scale encoding: {enc}"),
232
        }
233
103k
    }
234
235
    /// Return the hardware encoding of this [`Scale`].
236
100k
    fn enc(&self) -> u8 {
237
100k
        match self {
238
100k
            Scale::One => 0b00,
239
6
            Scale::Two => 0b01,
240
307
            Scale::Four => 0b10,
241
290
            Scale::Eight => 0b11,
242
        }
243
100k
    }
244
245
    /// Return how much this [`Scale`] will shift the value in the index
246
    /// register of the SIB byte.
247
    ///
248
    /// This is useful for pretty-printing; when encoding, one usually needs
249
    /// [`Scale::enc`].
250
0
    fn shift(&self) -> u8 {
251
0
        1 << self.enc()
252
0
    }
253
}
254
255
/// A general-purpose register or memory operand.
256
#[derive(Copy, Clone, Debug, PartialEq)]
257
#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
258
#[allow(
259
    clippy::module_name_repetitions,
260
    reason = "'GprMem' indicates this has GPR and memory variants"
261
)]
262
pub enum GprMem<R: AsReg, M: AsReg> {
263
    Gpr(R),
264
    Mem(Amode<M>),
265
}
266
267
impl<R: AsReg, M: AsReg> GprMem<R, M> {
268
    /// Pretty-print the operand.
269
0
    pub fn to_string(&self, size: Size) -> String {
270
0
        match self {
271
0
            GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
272
0
            GprMem::Mem(amode) => amode.to_string(),
273
        }
274
0
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
275
276
    /// Return the [`RexPrefix`] for each variant of this [`GprMem`].
277
    #[must_use]
278
10.3M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
279
10.3M
        match self {
280
4.32M
            GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
281
6.01M
            GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
282
        }
283
10.3M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _>>::as_rex_prefix
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
278
4.73M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
279
4.73M
        match self {
280
1.77M
            GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
281
2.95M
            GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
282
        }
283
4.73M
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
278
4.93M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
279
4.93M
        match self {
280
1.98M
            GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
281
2.95M
            GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
282
        }
283
4.93M
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
278
662k
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
279
662k
        match self {
280
559k
            GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
281
102k
            GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
282
        }
283
662k
    }
284
285
    /// Emit the ModR/M, SIB, and displacement suffixes for this [`GprMem`].
286
10.5M
    pub(crate) fn encode_rex_suffixes(
287
10.5M
        &self,
288
10.5M
        sink: &mut impl CodeSink,
289
10.5M
        enc_reg: u8,
290
10.5M
        bytes_at_end: u8,
291
10.5M
        evex_scaling: Option<i8>,
292
10.5M
    ) {
293
10.5M
        match self {
294
4.55M
            GprMem::Gpr(gpr) => {
295
4.55M
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
296
4.55M
            }
297
6.01M
            GprMem::Mem(amode) => {
298
6.01M
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
299
6.01M
            }
300
        }
301
10.5M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _>>::encode_rex_suffixes::<_>
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
286
4.76M
    pub(crate) fn encode_rex_suffixes(
287
4.76M
        &self,
288
4.76M
        sink: &mut impl CodeSink,
289
4.76M
        enc_reg: u8,
290
4.76M
        bytes_at_end: u8,
291
4.76M
        evex_scaling: Option<i8>,
292
4.76M
    ) {
293
4.76M
        match self {
294
1.80M
            GprMem::Gpr(gpr) => {
295
1.80M
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
296
1.80M
            }
297
2.95M
            GprMem::Mem(amode) => {
298
2.95M
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
299
2.95M
            }
300
        }
301
4.76M
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
286
5.13M
    pub(crate) fn encode_rex_suffixes(
287
5.13M
        &self,
288
5.13M
        sink: &mut impl CodeSink,
289
5.13M
        enc_reg: u8,
290
5.13M
        bytes_at_end: u8,
291
5.13M
        evex_scaling: Option<i8>,
292
5.13M
    ) {
293
5.13M
        match self {
294
2.18M
            GprMem::Gpr(gpr) => {
295
2.18M
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
296
2.18M
            }
297
2.95M
            GprMem::Mem(amode) => {
298
2.95M
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
299
2.95M
            }
300
        }
301
5.13M
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
286
662k
    pub(crate) fn encode_rex_suffixes(
287
662k
        &self,
288
662k
        sink: &mut impl CodeSink,
289
662k
        enc_reg: u8,
290
662k
        bytes_at_end: u8,
291
662k
        evex_scaling: Option<i8>,
292
662k
    ) {
293
662k
        match self {
294
559k
            GprMem::Gpr(gpr) => {
295
559k
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
296
559k
            }
297
102k
            GprMem::Mem(amode) => {
298
102k
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
299
102k
            }
300
        }
301
662k
    }
302
303
    /// Same as `XmmMem::encode_bx_regs`, but for `GprMem`.
304
231k
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
305
231k
        match self {
306
230k
            GprMem::Gpr(reg) => (Some(reg.enc()), None),
307
1.12k
            GprMem::Mem(amode) => amode.encode_bx_regs(),
308
        }
309
231k
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _>>::encode_bx_regs
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Line
Count
Source
304
32.1k
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
305
32.1k
        match self {
306
32.1k
            GprMem::Gpr(reg) => (Some(reg.enc()), None),
307
0
            GprMem::Mem(amode) => amode.encode_bx_regs(),
308
        }
309
32.1k
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Line
Count
Source
304
199k
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
305
199k
        match self {
306
198k
            GprMem::Gpr(reg) => (Some(reg.enc()), None),
307
1.12k
            GprMem::Mem(amode) => amode.encode_bx_regs(),
308
        }
309
199k
    }
310
}
311
312
impl<R: AsReg, M: AsReg> From<R> for GprMem<R, M> {
313
1.84M
    fn from(reg: R) -> GprMem<R, M> {
314
1.84M
        GprMem::Gpr(reg)
315
1.84M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::args::Gpr>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::external::PairedGpr>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _> as core::convert::From<_>>::from
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Gpr>>>::from
Line
Count
Source
313
1.64M
    fn from(reg: R) -> GprMem<R, M> {
314
1.64M
        GprMem::Gpr(reg)
315
1.64M
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::args::Gpr>>::from
Line
Count
Source
313
193k
    fn from(reg: R) -> GprMem<R, M> {
314
193k
        GprMem::Gpr(reg)
315
193k
    }
<cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::external::PairedGpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::external::PairedGpr>>::from
Line
Count
Source
313
6.17k
    fn from(reg: R) -> GprMem<R, M> {
314
6.17k
        GprMem::Gpr(reg)
315
6.17k
    }
316
}
317
318
impl<R: AsReg, M: AsReg> From<Amode<M>> for GprMem<R, M> {
319
0
    fn from(amode: Amode<M>) -> GprMem<R, M> {
320
0
        GprMem::Mem(amode)
321
0
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<_, _> as core::convert::From<cranelift_assembler_x64::mem::Amode<_>>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::GprMem<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_assembler_x64::mem::Amode<cranelift_codegen::isa::x64::inst::args::Gpr>>>::from
322
}
323
324
/// An XMM register or memory operand.
325
#[derive(Copy, Clone, Debug)]
326
#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
327
#[allow(
328
    clippy::module_name_repetitions,
329
    reason = "'XmmMem' indicates this has Xmm and memory variants"
330
)]
331
pub enum XmmMem<R: AsReg, M: AsReg> {
332
    Xmm(R),
333
    Mem(Amode<M>),
334
}
335
336
impl<R: AsReg, M: AsReg> XmmMem<R, M> {
337
    /// Pretty-print the operand.
338
0
    pub fn to_string(&self) -> String {
339
0
        match self {
340
0
            XmmMem::Xmm(xmm) => xmm.to_string(None),
341
0
            XmmMem::Mem(amode) => amode.to_string(),
342
        }
343
0
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<_, _>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::to_string
344
345
    /// Return the [`RexPrefix`] for each variant of this [`XmmMem`].
346
    #[must_use]
347
1.24M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
348
1.24M
        match self {
349
669k
            XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
350
578k
            XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
351
        }
352
1.24M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<_, _>>::as_rex_prefix
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
347
83.7k
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
348
83.7k
        match self {
349
0
            XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
350
83.7k
            XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
351
        }
352
83.7k
    }
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::as_rex_prefix
Line
Count
Source
347
1.16M
    pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
348
1.16M
        match self {
349
669k
            XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
350
494k
            XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
351
        }
352
1.16M
    }
353
354
    /// Emit the ModR/M, SIB, and displacement suffixes for this [`XmmMem`].
355
2.02M
    pub(crate) fn encode_rex_suffixes(
356
2.02M
        &self,
357
2.02M
        sink: &mut impl CodeSink,
358
2.02M
        enc_reg: u8,
359
2.02M
        bytes_at_end: u8,
360
2.02M
        evex_scaling: Option<i8>,
361
2.02M
    ) {
362
2.02M
        match self {
363
1.39M
            XmmMem::Xmm(xmm) => {
364
1.39M
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111));
365
1.39M
            }
366
622k
            XmmMem::Mem(amode) => {
367
622k
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
368
622k
            }
369
        }
370
2.02M
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<_, _>>::encode_rex_suffixes::<_>
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
355
83.7k
    pub(crate) fn encode_rex_suffixes(
356
83.7k
        &self,
357
83.7k
        sink: &mut impl CodeSink,
358
83.7k
        enc_reg: u8,
359
83.7k
        bytes_at_end: u8,
360
83.7k
        evex_scaling: Option<i8>,
361
83.7k
    ) {
362
83.7k
        match self {
363
0
            XmmMem::Xmm(xmm) => {
364
0
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111));
365
0
            }
366
83.7k
            XmmMem::Mem(amode) => {
367
83.7k
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
368
83.7k
            }
369
        }
370
83.7k
    }
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_rex_suffixes::<cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
355
1.93M
    pub(crate) fn encode_rex_suffixes(
356
1.93M
        &self,
357
1.93M
        sink: &mut impl CodeSink,
358
1.93M
        enc_reg: u8,
359
1.93M
        bytes_at_end: u8,
360
1.93M
        evex_scaling: Option<i8>,
361
1.93M
    ) {
362
1.93M
        match self {
363
1.39M
            XmmMem::Xmm(xmm) => {
364
1.39M
                sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111));
365
1.39M
            }
366
539k
            XmmMem::Mem(amode) => {
367
539k
                amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
368
539k
            }
369
        }
370
1.93M
    }
371
372
    /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX
373
    /// prefix).
374
    ///
375
    /// During encoding, the `b` bit is set by the topmost bit (the fourth bit)
376
    /// of either the `reg` register or, if this is a memory address, the `base`
377
    /// register. The `x` bit is set by the `index` register, when used.
378
774k
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
379
774k
        match self {
380
729k
            XmmMem::Xmm(reg) => (Some(reg.enc()), None),
381
44.4k
            XmmMem::Mem(amode) => amode.encode_bx_regs(),
382
        }
383
774k
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<_, _>>::encode_bx_regs
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::machinst::reg::Writable<cranelift_codegen::isa::x64::inst::args::Xmm>, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr>>::encode_bx_regs
Line
Count
Source
378
774k
    pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
379
774k
        match self {
380
729k
            XmmMem::Xmm(reg) => (Some(reg.enc()), None),
381
44.4k
            XmmMem::Mem(amode) => amode.encode_bx_regs(),
382
        }
383
774k
    }
384
}
385
386
impl<R: AsReg, M: AsReg> From<R> for XmmMem<R, M> {
387
127k
    fn from(reg: R) -> XmmMem<R, M> {
388
127k
        XmmMem::Xmm(reg)
389
127k
    }
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::args::Xmm>>::from
Unexecuted instantiation: <cranelift_assembler_x64::mem::XmmMem<_, _> as core::convert::From<_>>::from
<cranelift_assembler_x64::mem::XmmMem<cranelift_codegen::isa::x64::inst::args::Xmm, cranelift_codegen::isa::x64::inst::args::Gpr> as core::convert::From<cranelift_codegen::isa::x64::inst::args::Xmm>>::from
Line
Count
Source
387
127k
    fn from(reg: R) -> XmmMem<R, M> {
388
127k
        XmmMem::Xmm(reg)
389
127k
    }
390
}
391
392
impl<R: AsReg, M: AsReg> From<Amode<M>> for XmmMem<R, M> {
393
0
    fn from(amode: Amode<M>) -> XmmMem<R, M> {
394
0
        XmmMem::Mem(amode)
395
0
    }
396
}
397
398
/// Emit the ModRM/SIB/displacement sequence for a memory operand.
399
8.70M
pub fn emit_modrm_sib_disp<R: AsReg>(
400
8.70M
    sink: &mut impl CodeSink,
401
8.70M
    enc_g: u8,
402
8.70M
    mem_e: &Amode<R>,
403
8.70M
    bytes_at_end: u8,
404
8.70M
    evex_scaling: Option<i8>,
405
8.70M
) {
406
8.70M
    match *mem_e {
407
8.53M
        Amode::ImmReg { simm32, base, .. } => {
408
8.53M
            let enc_e = base.enc();
409
8.53M
            let mut imm = Disp::new(simm32.value(sink), evex_scaling);
410
411
            // Most base registers allow for a single ModRM byte plus an
412
            // optional immediate. If rsp is the base register, however, then a
413
            // SIB byte must be used.
414
8.53M
            let enc_e_low3 = enc_e & 7;
415
8.53M
            if enc_e_low3 == gpr::enc::RSP {
416
3.88M
                // Displacement from RSP is encoded with a SIB byte where
417
3.88M
                // the index and base are both encoded as RSP's encoding of
418
3.88M
                // 0b100. This special encoding means that the index register
419
3.88M
                // isn't used and the base is 0b100 with or without a
420
3.88M
                // REX-encoded 4th bit (e.g. rsp or r12)
421
3.88M
                sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
422
3.88M
                sink.put1(0b00_100_100);
423
3.88M
                imm.emit(sink);
424
3.88M
            } else {
425
                // If the base register is rbp and there's no offset then force
426
                // a 1-byte zero offset since otherwise the encoding would be
427
                // invalid.
428
4.65M
                if enc_e_low3 == gpr::enc::RBP {
429
129k
                    imm.force_immediate();
430
4.52M
                }
431
4.65M
                sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
432
4.65M
                imm.emit(sink);
433
            }
434
        }
435
436
        Amode::ImmRegRegShift {
437
100k
            simm32,
438
100k
            base,
439
100k
            index,
440
100k
            scale,
441
            ..
442
        } => {
443
100k
            let enc_base = base.enc();
444
100k
            let enc_index = index.enc();
445
446
            // Encoding of ModRM/SIB bytes don't allow the index register to
447
            // ever be rsp. Note, though, that the encoding of r12, whose three
448
            // lower bits match the encoding of rsp, is explicitly allowed with
449
            // REX bytes so only rsp is disallowed.
450
100k
            assert!(enc_index != gpr::enc::RSP);
451
452
            // If the offset is zero then there is no immediate. Note, though,
453
            // that if the base register's lower three bits are `101` then an
454
            // offset must be present. This is a special case in the encoding of
455
            // the SIB byte and requires an explicit displacement with rbp/r13.
456
100k
            let mut imm = Disp::new(simm32.value(), evex_scaling);
457
100k
            if enc_base & 7 == gpr::enc::RBP {
458
48
                imm.force_immediate();
459
100k
            }
460
461
            // With the above determined encode the ModRM byte, then the SIB
462
            // byte, then any immediate as necessary.
463
100k
            sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
464
100k
            sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
465
100k
            imm.emit(sink);
466
        }
467
468
70.9k
        Amode::RipRelative { target } => {
469
70.9k
            // RIP-relative is mod=00, rm=101.
470
70.9k
            sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
471
70.9k
472
70.9k
            // Inform the code sink about the RIP-relative `target` at the
473
70.9k
            // current offset, emitting a `LabelUse`, a relocation, or etc as
474
70.9k
            // appropriate.
475
70.9k
            sink.use_target(target);
476
70.9k
477
70.9k
            // N.B.: some instructions (XmmRmRImm format for example)
478
70.9k
            // have bytes *after* the RIP-relative offset. The
479
70.9k
            // addressed location is relative to the end of the
480
70.9k
            // instruction, but the relocation is nominally relative
481
70.9k
            // to the end of the u32 field. So, to compensate for
482
70.9k
            // this, we emit a negative extra offset in the u32 field
483
70.9k
            // initially, and the relocation will add to it.
484
70.9k
            sink.put4(-(i32::from(bytes_at_end)) as u32);
485
70.9k
        }
486
    }
487
8.70M
}
Unexecuted instantiation: cranelift_assembler_x64::mem::emit_modrm_sib_disp::<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Unexecuted instantiation: cranelift_assembler_x64::mem::emit_modrm_sib_disp::<_, _>
cranelift_assembler_x64::mem::emit_modrm_sib_disp::<cranelift_codegen::isa::x64::inst::args::Gpr, cranelift_codegen::isa::x64::inst::external::AsmCodeSink>
Line
Count
Source
399
8.70M
pub fn emit_modrm_sib_disp<R: AsReg>(
400
8.70M
    sink: &mut impl CodeSink,
401
8.70M
    enc_g: u8,
402
8.70M
    mem_e: &Amode<R>,
403
8.70M
    bytes_at_end: u8,
404
8.70M
    evex_scaling: Option<i8>,
405
8.70M
) {
406
8.70M
    match *mem_e {
407
8.53M
        Amode::ImmReg { simm32, base, .. } => {
408
8.53M
            let enc_e = base.enc();
409
8.53M
            let mut imm = Disp::new(simm32.value(sink), evex_scaling);
410
411
            // Most base registers allow for a single ModRM byte plus an
412
            // optional immediate. If rsp is the base register, however, then a
413
            // SIB byte must be used.
414
8.53M
            let enc_e_low3 = enc_e & 7;
415
8.53M
            if enc_e_low3 == gpr::enc::RSP {
416
3.88M
                // Displacement from RSP is encoded with a SIB byte where
417
3.88M
                // the index and base are both encoded as RSP's encoding of
418
3.88M
                // 0b100. This special encoding means that the index register
419
3.88M
                // isn't used and the base is 0b100 with or without a
420
3.88M
                // REX-encoded 4th bit (e.g. rsp or r12)
421
3.88M
                sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
422
3.88M
                sink.put1(0b00_100_100);
423
3.88M
                imm.emit(sink);
424
3.88M
            } else {
425
                // If the base register is rbp and there's no offset then force
426
                // a 1-byte zero offset since otherwise the encoding would be
427
                // invalid.
428
4.65M
                if enc_e_low3 == gpr::enc::RBP {
429
129k
                    imm.force_immediate();
430
4.52M
                }
431
4.65M
                sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
432
4.65M
                imm.emit(sink);
433
            }
434
        }
435
436
        Amode::ImmRegRegShift {
437
100k
            simm32,
438
100k
            base,
439
100k
            index,
440
100k
            scale,
441
            ..
442
        } => {
443
100k
            let enc_base = base.enc();
444
100k
            let enc_index = index.enc();
445
446
            // Encoding of ModRM/SIB bytes don't allow the index register to
447
            // ever be rsp. Note, though, that the encoding of r12, whose three
448
            // lower bits match the encoding of rsp, is explicitly allowed with
449
            // REX bytes so only rsp is disallowed.
450
100k
            assert!(enc_index != gpr::enc::RSP);
451
452
            // If the offset is zero then there is no immediate. Note, though,
453
            // that if the base register's lower three bits are `101` then an
454
            // offset must be present. This is a special case in the encoding of
455
            // the SIB byte and requires an explicit displacement with rbp/r13.
456
100k
            let mut imm = Disp::new(simm32.value(), evex_scaling);
457
100k
            if enc_base & 7 == gpr::enc::RBP {
458
48
                imm.force_immediate();
459
100k
            }
460
461
            // With the above determined encode the ModRM byte, then the SIB
462
            // byte, then any immediate as necessary.
463
100k
            sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
464
100k
            sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
465
100k
            imm.emit(sink);
466
        }
467
468
70.9k
        Amode::RipRelative { target } => {
469
70.9k
            // RIP-relative is mod=00, rm=101.
470
70.9k
            sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
471
70.9k
472
70.9k
            // Inform the code sink about the RIP-relative `target` at the
473
70.9k
            // current offset, emitting a `LabelUse`, a relocation, or etc as
474
70.9k
            // appropriate.
475
70.9k
            sink.use_target(target);
476
70.9k
477
70.9k
            // N.B.: some instructions (XmmRmRImm format for example)
478
70.9k
            // have bytes *after* the RIP-relative offset. The
479
70.9k
            // addressed location is relative to the end of the
480
70.9k
            // instruction, but the relocation is nominally relative
481
70.9k
            // to the end of the u32 field. So, to compensate for
482
70.9k
            // this, we emit a negative extra offset in the u32 field
483
70.9k
            // initially, and the relocation will add to it.
484
70.9k
            sink.put4(-(i32::from(bytes_at_end)) as u32);
485
70.9k
        }
486
    }
487
8.70M
}