Skip to main content

cranelift_assembler_x64/
imm.rs

1//! Immediate operands to instructions.
2
3use crate::api::CodeSink;
4use alloc::{format, string::String};
5use core::fmt;
6
7/// This helper function prints the unsigned hexadecimal representation of the
8/// immediate value: e.g., this prints `$0xfe` to represent both the signed `-2`
9/// and the unsigned `254`.
10macro_rules! hexify {
11    ($n:expr) => {
12        format!("$0x{:x}", $n)
13    };
14}
15
16/// Like `hexify!`, but this performs a sign extension.
17macro_rules! hexify_sign_extend {
18    ($n:expr, $from:ty => $to:ty) => {{
19        let from: $from = $n; // Assert the type we expect.
20        let to = <$to>::from(from);
21        format!("$0x{:x}", to)
22    }};
23}
24
25/// An 8-bit immediate operand.
26#[derive(Clone, Copy, Debug)]
27#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
28pub struct Imm8(u8);
29
30impl Imm8 {
31    #[must_use]
32    pub fn new(value: u8) -> Self {
33        Self(value)
34    }
35
36    #[must_use]
37    pub fn value(&self) -> u8 {
38        self.0
39    }
40
41    pub fn encode(&self, sink: &mut impl CodeSink) {
42        sink.put1(self.0);
43    }
44}
45
46impl From<u8> for Imm8 {
47    fn from(imm8: u8) -> Self {
48        Self(imm8)
49    }
50}
51
52impl TryFrom<i32> for Imm8 {
53    type Error = core::num::TryFromIntError;
54    fn try_from(simm32: i32) -> Result<Self, Self::Error> {
55        Ok(Self(u8::try_from(simm32)?))
56    }
57}
58
59impl fmt::Display for Imm8 {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        write!(f, "$0x{:x}", self.0)
62    }
63}
64
65/// A _signed_ 8-bit immediate operand (suitable for sign extension).
66#[derive(Clone, Copy, Debug)]
67#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
68pub struct Simm8(i8);
69
70impl Simm8 {
71    #[must_use]
72    pub fn new(value: i8) -> Self {
73        Self(value)
74    }
75
76    #[must_use]
77    pub fn value(&self) -> i8 {
78        self.0
79    }
80
81    pub fn encode(&self, sink: &mut impl CodeSink) {
82        sink.put1(self.0 as u8);
83    }
84
85    #[must_use]
86    pub fn to_string(&self, extend: Extension) -> String {
87        use Extension::{None, SignExtendLong, SignExtendQuad, SignExtendWord};
88        match extend {
89            None => hexify!(self.0),
90            SignExtendWord => hexify_sign_extend!(self.0, i8 => i16),
91            SignExtendLong => hexify_sign_extend!(self.0, i8 => i32),
92            SignExtendQuad => hexify_sign_extend!(self.0, i8 => i64),
93        }
94    }
95}
96
97impl From<i8> for Simm8 {
98    fn from(simm8: i8) -> Self {
99        Self(simm8)
100    }
101}
102
103impl TryFrom<i32> for Simm8 {
104    type Error = core::num::TryFromIntError;
105    fn try_from(simm32: i32) -> Result<Self, Self::Error> {
106        Ok(Self(i8::try_from(simm32)?))
107    }
108}
109
110/// A 16-bit immediate operand.
111#[derive(Copy, Clone, Debug)]
112#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
113pub struct Imm16(u16);
114
115impl Imm16 {
116    #[must_use]
117    pub fn new(value: u16) -> Self {
118        Self(value)
119    }
120
121    #[must_use]
122    pub fn value(&self) -> u16 {
123        self.0
124    }
125
126    pub fn encode(&self, sink: &mut impl CodeSink) {
127        sink.put2(self.0);
128    }
129}
130
131impl From<u16> for Imm16 {
132    fn from(imm16: u16) -> Self {
133        Self(imm16)
134    }
135}
136
137impl TryFrom<i32> for Imm16 {
138    type Error = core::num::TryFromIntError;
139    fn try_from(simm32: i32) -> Result<Self, Self::Error> {
140        Ok(Self(u16::try_from(simm32)?))
141    }
142}
143
144impl fmt::Display for Imm16 {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        write!(f, "$0x{:x}", self.0)
147    }
148}
149
150/// A _signed_ 16-bit immediate operand (suitable for sign extension).
151#[derive(Copy, Clone, Debug)]
152#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
153pub struct Simm16(i16);
154
155impl Simm16 {
156    #[must_use]
157    pub fn new(value: i16) -> Self {
158        Self(value)
159    }
160
161    #[must_use]
162    pub fn value(&self) -> i16 {
163        self.0
164    }
165
166    pub fn encode(&self, sink: &mut impl CodeSink) {
167        sink.put2(self.0 as u16);
168    }
169
170    #[must_use]
171    pub fn to_string(&self, extend: Extension) -> String {
172        use Extension::{None, SignExtendLong, SignExtendQuad, SignExtendWord};
173        match extend {
174            None => hexify!(self.0),
175            SignExtendWord => unreachable!("the 16-bit value is already 16 bits"),
176            SignExtendLong => hexify_sign_extend!(self.0, i16 => i32),
177            SignExtendQuad => hexify_sign_extend!(self.0, i16 => i64),
178        }
179    }
180}
181
182impl From<i16> for Simm16 {
183    fn from(simm16: i16) -> Self {
184        Self(simm16)
185    }
186}
187
188impl TryFrom<i32> for Simm16 {
189    type Error = core::num::TryFromIntError;
190    fn try_from(simm32: i32) -> Result<Self, Self::Error> {
191        Ok(Self(i16::try_from(simm32)?))
192    }
193}
194
195/// A 32-bit immediate operand.
196///
197/// Note that, "in 64-bit mode, the typical size of immediate operands remains
198/// 32 bits. When the operand size is 64 bits, the processor sign-extends all
199/// immediates to 64 bits prior to their use" (Intel SDM Vol. 2, 2.2.1.5).
200#[derive(Copy, Clone, Debug)]
201#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
202pub struct Imm32(u32);
203
204impl Imm32 {
205    #[must_use]
206    pub fn new(value: u32) -> Self {
207        Self(value)
208    }
209
210    #[must_use]
211    pub fn value(&self) -> u32 {
212        self.0
213    }
214
215    pub fn encode(&self, sink: &mut impl CodeSink) {
216        sink.put4(self.0);
217    }
218}
219
220impl From<u32> for Imm32 {
221    fn from(imm32: u32) -> Self {
222        Self(imm32)
223    }
224}
225
226impl From<i32> for Imm32 {
227    fn from(simm32: i32) -> Self {
228        // TODO: should this be a `TryFrom`?
229        Self(simm32 as u32)
230    }
231}
232
233impl fmt::Display for Imm32 {
234    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235        write!(f, "$0x{:x}", self.0)
236    }
237}
238
239/// A _signed_ 32-bit immediate operand (suitable for sign extension).
240///
241/// Note that, "in 64-bit mode, the typical size of immediate operands remains
242/// 32 bits. When the operand size is 64 bits, the processor sign-extends all
243/// immediates to 64 bits prior to their use" (Intel SDM Vol. 2, 2.2.1.5).
244#[derive(Copy, Clone, Debug)]
245#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
246pub struct Simm32(i32);
247
248impl Simm32 {
249    #[must_use]
250    pub fn new(value: i32) -> Self {
251        Self(value)
252    }
253
254    #[must_use]
255    pub fn value(&self) -> i32 {
256        self.0
257    }
258
259    pub fn encode(&self, sink: &mut impl CodeSink) {
260        sink.put4(self.0 as u32);
261    }
262
263    #[must_use]
264    pub fn to_string(&self, extend: Extension) -> String {
265        use Extension::{None, SignExtendLong, SignExtendQuad, SignExtendWord};
266        match extend {
267            None => hexify!(self.0),
268            SignExtendWord => unreachable!("cannot sign extend a 32-bit value to 16 bits"),
269            SignExtendLong => unreachable!("the 32-bit value is already 32 bits"),
270            SignExtendQuad => hexify_sign_extend!(self.0, i32 => i64),
271        }
272    }
273}
274
275impl From<i32> for Simm32 {
276    fn from(simm32: i32) -> Self {
277        Self(simm32)
278    }
279}
280
281/// A 64-bit immediate operand.
282///
283/// This form is quite rare; see certain `mov` instructions.
284#[derive(Copy, Clone, Debug)]
285#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
286pub struct Imm64(u64);
287
288impl Imm64 {
289    #[must_use]
290    pub fn new(value: u64) -> Self {
291        Self(value)
292    }
293
294    #[must_use]
295    pub fn value(&self) -> u64 {
296        self.0
297    }
298
299    pub fn encode(&self, sink: &mut impl CodeSink) {
300        sink.put8(self.0);
301    }
302}
303
304impl From<u64> for Imm64 {
305    fn from(imm64: u64) -> Self {
306        Self(imm64)
307    }
308}
309
310impl fmt::Display for Imm64 {
311    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312        write!(f, "$0x{:x}", self.0)
313    }
314}
315
316/// Define the ways an immediate may be sign- or zero-extended.
317#[derive(Clone, Copy, Debug)]
318pub enum Extension {
319    None,
320    SignExtendQuad,
321    SignExtendLong,
322    SignExtendWord,
323}