cranelift_assembler_x64/
imm.rs

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