osom_asm_x86_64/models/
memory.rs

1use osom_encoders_x86_64::models as enc_models;
2
3use super::{GPR, Immediate, Label, Scale, Size};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6#[must_use]
7#[repr(u8)]
8pub(crate) enum MemoryImpl {
9    Based {
10        base: GPR,
11        offset: Immediate,
12    },
13    Scaled {
14        index: GPR,
15        scale: Scale,
16        offset: Immediate,
17    },
18    BasedScaled {
19        base: GPR,
20        index: GPR,
21        scale: Scale,
22        offset: Immediate,
23    },
24    Label {
25        label: Label,
26    },
27}
28
29/// Represents a general `x86_64` memory operand.
30#[derive(Debug, Clone, PartialEq, Eq, Hash)]
31#[must_use]
32#[repr(transparent)]
33pub struct Memory {
34    value: MemoryImpl,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38#[repr(u8)]
39#[must_use]
40pub enum NewMemoryError {
41    GPRNotBit64,
42    RSPNotAllowedAsIndex,
43}
44
45impl Memory {
46    #[inline]
47    pub fn based(base: GPR, offset: Immediate) -> Result<Self, NewMemoryError> {
48        if base.size() != Size::Bit64 {
49            return Err(NewMemoryError::GPRNotBit64);
50        }
51
52        Ok(Self {
53            value: MemoryImpl::Based { base, offset },
54        })
55    }
56
57    #[inline]
58    pub fn scaled(index: GPR, scale: Scale, offset: Immediate) -> Result<Self, NewMemoryError> {
59        if index.size() != Size::Bit64 {
60            return Err(NewMemoryError::GPRNotBit64);
61        }
62
63        if index == GPR::RSP {
64            return Err(NewMemoryError::RSPNotAllowedAsIndex);
65        }
66
67        Ok(Self {
68            value: MemoryImpl::Scaled { index, scale, offset },
69        })
70    }
71
72    #[inline]
73    pub fn based_scaled(base: GPR, index: GPR, scale: Scale, offset: Immediate) -> Result<Self, NewMemoryError> {
74        if base.size() != Size::Bit64 {
75            return Err(NewMemoryError::GPRNotBit64);
76        }
77
78        if index.size() != Size::Bit64 {
79            return Err(NewMemoryError::GPRNotBit64);
80        }
81
82        if index == GPR::RSP {
83            return Err(NewMemoryError::RSPNotAllowedAsIndex);
84        }
85
86        Ok(Self {
87            value: MemoryImpl::BasedScaled {
88                base,
89                index,
90                scale,
91                offset,
92            },
93        })
94    }
95
96    /// This will get translated to RIP-relative address.
97    #[inline(always)]
98    pub const fn label(label: Label) -> Self {
99        Self {
100            value: MemoryImpl::Label { label },
101        }
102    }
103
104    #[inline(always)]
105    pub(crate) fn get_label(&self) -> Option<&Label> {
106        match &self.value {
107            MemoryImpl::Label { label } => Some(label),
108            _ => None,
109        }
110    }
111
112    pub(crate) fn as_enc_mem(&self) -> enc_models::Memory {
113        const fn imm_to_offset(offset: Immediate) -> enc_models::Offset {
114            let val = offset.value();
115            if val == 0 {
116                return enc_models::Offset::None;
117            }
118
119            match offset.real_size() {
120                Size::Bit8 => enc_models::Offset::Bit8(enc_models::Immediate8::from_i8(val as i8)),
121                Size::Bit16 | Size::Bit32 => enc_models::Offset::Bit32(enc_models::Immediate32::from_i32(val)),
122                _ => panic!("Invalid offset size"),
123            }
124        }
125
126        match &self.value {
127            MemoryImpl::Based { base, offset } => enc_models::Memory::Based {
128                base: base.as_enc_gpr(),
129                offset: imm_to_offset(*offset),
130            },
131            MemoryImpl::Scaled { index, scale, offset } => enc_models::Memory::Scaled {
132                index: index.as_enc_gpr(),
133                scale: scale.as_enc_scale(),
134                offset: imm_to_offset(*offset),
135            },
136            MemoryImpl::BasedScaled {
137                base,
138                index,
139                scale,
140                offset,
141            } => enc_models::Memory::BasedScaled {
142                base: base.as_enc_gpr(),
143                index: index.as_enc_gpr(),
144                scale: scale.as_enc_scale(),
145                offset: imm_to_offset(*offset),
146            },
147            MemoryImpl::Label { label: _ } => {
148                // We set offset to None. It will be patched later.
149                enc_models::Memory::RelativeToRIP {
150                    offset: enc_models::Offset::None,
151                }
152            }
153        }
154    }
155}