Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/assembler-x64/src/rex.rs
1692 views
1
//! Encoding logic for REX instructions.
2
3
use crate::api::CodeSink;
4
5
fn low8_will_sign_extend_to_32(xs: i32) -> bool {
6
xs == ((xs << 24) >> 24)
7
}
8
9
/// Encode the ModR/M byte.
10
#[inline]
11
pub(crate) fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 {
12
debug_assert!(m0d < 4);
13
debug_assert!(enc_reg_g < 8);
14
debug_assert!(rm_e < 8);
15
((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7)
16
}
17
18
/// Encode the SIB byte (scale-index-base).
19
#[inline]
20
pub(crate) fn encode_sib(scale: u8, enc_index: u8, enc_base: u8) -> u8 {
21
debug_assert!(scale < 4);
22
debug_assert!(enc_index < 8);
23
debug_assert!(enc_base < 8);
24
((scale & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7)
25
}
26
27
/// Tests whether `enc` is `rsp`, `rbp`, `rsi`, or `rdi`. If 8-bit register
28
/// sizes are used then it means a REX prefix is required.
29
///
30
/// This function is used below in combination with `uses_8bit` booleans to
31
/// determine the `RexPrefix::must_emit` flag. Table 3-2 in volume 1 of the
32
/// Intel manual details how referencing `dil`, the low 8-bits of `rdi`,
33
/// requires the use of the REX prefix as without it it would otherwise
34
/// reference the `AH` register.
35
///
36
/// This is used whenever a register is encoded with a `RexPrefix` and is also
37
/// only used if the register is referenced in its 8-bit form. That means for
38
/// example that when encoding addressing modes this function is not used.
39
/// Addressing modes use 64-bit versions of registers meaning that the 8-bit
40
/// special case does not apply.
41
const fn is_special_if_8bit(enc: u8) -> bool {
42
enc >= 4 && enc <= 7
43
}
44
45
/// Construct and emit the REX prefix byte.
46
///
47
/// For more details, see section 2.2.1, "REX Prefixes" in Intel's reference
48
/// manual.
49
#[derive(Clone, Copy)]
50
pub struct RexPrefix {
51
byte: u8,
52
must_emit: bool,
53
}
54
55
impl RexPrefix {
56
/// Construct the [`RexPrefix`] for a unary instruction.
57
///
58
/// Used with a single register operand:
59
/// - `x` and `r` are unused.
60
/// - `b` extends the `reg` register, allowing access to r8-r15, or the top
61
/// bit of the opcode digit.
62
#[inline]
63
#[must_use]
64
pub const fn one_op(enc: u8, w_bit: bool, uses_8bit: bool) -> Self {
65
let must_emit = uses_8bit && is_special_if_8bit(enc);
66
let w = if w_bit { 1 } else { 0 };
67
let r = 0;
68
let x = 0;
69
let b = (enc >> 3) & 1;
70
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
71
Self {
72
byte: flag,
73
must_emit,
74
}
75
}
76
77
/// Construct the [`RexPrefix`] for a binary instruction.
78
///
79
/// Used without a SIB byte or for register-to-register addressing:
80
/// - `r` extends the `reg` operand, allowing access to r8-r15.
81
/// - `x` is unused.
82
/// - `b` extends the `r/m` operand, allowing access to r8-r15.
83
#[inline]
84
#[must_use]
85
pub const fn two_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
86
let mut ret = RexPrefix::mem_op(enc_reg, enc_rm, w_bit, uses_8bit);
87
if uses_8bit && is_special_if_8bit(enc_rm) {
88
ret.must_emit = true;
89
}
90
ret
91
}
92
93
/// Construct the [`RexPrefix`] for a binary instruction where one operand
94
/// is a memory address.
95
///
96
/// This is the same as [`RexPrefix::two_op`] except that `enc_rm` is
97
/// guaranteed to address a 64-bit register. This has a slightly different
98
/// meaning when `uses_8bit` is `true` to omit the REX prefix in more cases
99
/// than `two_op` would emit.
100
#[inline]
101
#[must_use]
102
pub const fn mem_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
103
let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
104
let w = if w_bit { 1 } else { 0 };
105
let r = (enc_reg >> 3) & 1;
106
let x = 0;
107
let b = (enc_rm >> 3) & 1;
108
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
109
Self {
110
byte: flag,
111
must_emit,
112
}
113
}
114
115
/// Construct the [`RexPrefix`] for an instruction using an opcode digit.
116
///
117
/// :
118
/// - `r` extends the opcode digit.
119
/// - `x` is unused.
120
/// - `b` extends the `reg` operand, allowing access to r8-r15.
121
#[inline]
122
#[must_use]
123
pub const fn with_digit(digit: u8, enc_reg: u8, w_bit: bool, uses_8bit: bool) -> Self {
124
Self::two_op(digit, enc_reg, w_bit, uses_8bit)
125
}
126
127
/// Construct the [`RexPrefix`] for a ternary instruction, typically using a
128
/// memory address.
129
///
130
/// Used with a SIB byte:
131
/// - `r` extends the `reg` operand, allowing access to r8-r15.
132
/// - `x` extends the index register, allowing access to r8-r15.
133
/// - `b` extends the base register, allowing access to r8-r15.
134
#[inline]
135
#[must_use]
136
pub const fn three_op(
137
enc_reg: u8,
138
enc_index: u8,
139
enc_base: u8,
140
w_bit: bool,
141
uses_8bit: bool,
142
) -> Self {
143
let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
144
let w = if w_bit { 1 } else { 0 };
145
let r = (enc_reg >> 3) & 1;
146
let x = (enc_index >> 3) & 1;
147
let b = (enc_base >> 3) & 1;
148
let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
149
Self {
150
byte: flag,
151
must_emit,
152
}
153
}
154
155
/// Possibly emit the REX prefix byte.
156
///
157
/// This will only be emitted if the REX prefix is not `0x40` (the default)
158
/// or if the instruction uses 8-bit operands.
159
#[inline]
160
pub fn encode(&self, sink: &mut impl CodeSink) {
161
if self.byte != 0x40 || self.must_emit {
162
sink.put1(self.byte);
163
}
164
}
165
}
166
167
/// The displacement bytes used after the ModR/M and SIB bytes.
168
#[derive(Copy, Clone)]
169
pub enum Disp {
170
None,
171
Imm8(i8),
172
Imm32(i32),
173
}
174
175
impl Disp {
176
/// Classifies the 32-bit immediate `val` as how this can be encoded
177
/// with ModRM/SIB bytes.
178
///
179
/// For `evex_scaling` according to Section 2.7.5 of Intel's manual:
180
///
181
/// > EVEX-encoded instructions always use a compressed displacement scheme
182
/// > by multiplying disp8 in conjunction with a scaling factor N that is
183
/// > determined based on the vector length, the value of EVEX.b bit
184
/// > (embedded broadcast) and the input element size of the instruction
185
///
186
/// The `evex_scaling` factor provided here is `Some(N)` for EVEX
187
/// instructions. This is taken into account where the `Imm` value
188
/// contained is the raw byte offset.
189
pub fn new(val: i32, evex_scaling: Option<i8>) -> Disp {
190
if val == 0 {
191
return Disp::None;
192
}
193
match evex_scaling {
194
Some(scaling) => {
195
if val % i32::from(scaling) == 0 {
196
let scaled = val / i32::from(scaling);
197
if low8_will_sign_extend_to_32(scaled) {
198
return Disp::Imm8(scaled as i8);
199
}
200
}
201
Disp::Imm32(val)
202
}
203
None => match i8::try_from(val) {
204
Ok(val) => Disp::Imm8(val),
205
Err(_) => Disp::Imm32(val),
206
},
207
}
208
}
209
210
/// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases
211
/// where some base registers require an immediate.
212
pub fn force_immediate(&mut self) {
213
if let Disp::None = self {
214
*self = Disp::Imm8(0);
215
}
216
}
217
218
/// Returns the two "mod" bits present at the upper bits of the mod/rm
219
/// byte.
220
pub fn m0d(self) -> u8 {
221
match self {
222
Disp::None => 0b00,
223
Disp::Imm8(_) => 0b01,
224
Disp::Imm32(_) => 0b10,
225
}
226
}
227
228
/// Emit the truncated immediate into the code sink.
229
pub fn emit(self, sink: &mut impl CodeSink) {
230
match self {
231
Disp::None => {}
232
Disp::Imm8(n) => sink.put1(n as u8),
233
Disp::Imm32(n) => sink.put4(n as u32),
234
}
235
}
236
}
237
238