Skip to content

Commit 19000ee

Browse files
authored
asm: refactor how REX prefixes are emitted (#10745)
This does not change functionality; instead, it reorganizes how REX prefixes were encoded by the assembler to simplify code generation. `RexFlags` now becomes the more-correct `RexPrefix` and constructors like `RexPrefix::two_op` build the necessary byte for emission with `RexPrefix::encode`.
1 parent 6cd9b4a commit 19000ee

File tree

6 files changed

+128
-180
lines changed

6 files changed

+128
-180
lines changed

cranelift/assembler-x64/meta/src/generate/format.rs

+17-82
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,14 @@ impl dsl::Format {
6868
fn generate_rex_prefix(&self, f: &mut Formatter, rex: &dsl::Rex) {
6969
use dsl::OperandKind::{FixedReg, Imm, Mem, Reg, RegMem};
7070
f.empty_line();
71-
f.comment("Emit REX prefix.");
71+
f.comment("Possibly emit REX prefix.");
7272

7373
let find_8bit_registers =
7474
|l: &dsl::Location| l.bits() == 8 && matches!(l.kind(), Reg(_) | RegMem(_));
75-
if self.locations().any(find_8bit_registers) {
76-
fmtln!(f, "let mut rex = {};", rex.generate_flags());
77-
for op in self.locations().copied().filter(find_8bit_registers) {
78-
fmtln!(f, "self.{op}.always_emit_if_8bit_needed(&mut rex);");
79-
}
80-
} else {
81-
fmtln!(f, "let rex = {};", rex.generate_flags());
82-
}
75+
let uses_8bit = self.locations().any(find_8bit_registers);
76+
fmtln!(f, "let uses_8bit = {uses_8bit};");
77+
fmtln!(f, "let w_bit = {};", rex.w);
78+
let bits = "w_bit, uses_8bit";
8379

8480
match self.operands_by_kind().as_slice() {
8581
[FixedReg(dst), Imm(_)] => {
@@ -89,92 +85,41 @@ impl dsl::Format {
8985
"we expect no digit for operands: [FixedReg, Imm]"
9086
);
9187
fmtln!(f, "let digit = 0;");
92-
fmtln!(f, "rex.emit_two_op(buf, digit, self.{dst}.enc());");
88+
fmtln!(f, "let dst = self.{dst}.enc();");
89+
fmtln!(f, "let rex = RexPrefix::with_digit(digit, dst, {bits});");
9390
}
9491
[Mem(dst), Imm(_)] => {
9592
let digit = rex
9693
.digit
9794
.expect("REX digit must be set for operands: [Mem, Imm]");
9895
fmtln!(f, "let digit = 0x{digit:x};");
99-
fmtln!(f, "self.{dst}.emit_rex_prefix(rex, digit, buf);");
96+
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(digit, {bits});");
10097
}
10198
[RegMem(dst), Imm(_)] => {
10299
let digit = rex
103100
.digit
104101
.expect("REX digit must be set for operands: [RegMem, Imm]");
105102
fmtln!(f, "let digit = 0x{digit:x};");
106-
f.add_block(&format!("match &self.{dst}"), |f| {
107-
fmtln!(
108-
f,
109-
"GprMem::Gpr({dst}) => rex.emit_two_op(buf, digit, {dst}.enc()),"
110-
);
111-
fmtln!(
112-
f,
113-
"GprMem::Mem({dst}) => {dst}.emit_rex_prefix(rex, digit, buf),"
114-
);
115-
});
103+
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(digit, {bits});");
116104
}
117105
[Reg(dst), RegMem(src)] => {
118-
fmtln!(f, "let {dst} = self.{dst}.enc();");
119-
f.add_block(&format!("match &self.{src}"), |f| {
120-
match dst.bits() {
121-
128 => {
122-
fmtln!(
123-
f,
124-
"XmmMem::Xmm({src}) => rex.emit_two_op(buf, {dst}, {src}.enc()),"
125-
);
126-
fmtln!(
127-
f,
128-
"XmmMem::Mem({src}) => {src}.emit_rex_prefix(rex, {dst}, buf),"
129-
);
130-
}
131-
_ => {
132-
fmtln!(
133-
f,
134-
"GprMem::Gpr({src}) => rex.emit_two_op(buf, {dst}, {src}.enc()),"
135-
);
136-
fmtln!(
137-
f,
138-
"GprMem::Mem({src}) => {src}.emit_rex_prefix(rex, {dst}, buf),"
139-
);
140-
}
141-
};
142-
});
106+
fmtln!(f, "let dst = self.{dst}.enc();");
107+
fmtln!(f, "let rex = self.{src}.as_rex_prefix(dst, {bits});");
143108
}
144109
[Mem(dst), Reg(src)] => {
145-
fmtln!(f, "let {src} = self.{src}.enc();");
146-
fmtln!(f, "self.{dst}.emit_rex_prefix(rex, {src}, buf);");
110+
fmtln!(f, "let src = self.{src}.enc();");
111+
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(src, {bits});");
147112
}
148113
[RegMem(dst), Reg(src)]
149114
| [RegMem(dst), Reg(src), Imm(_)]
150115
| [RegMem(dst), Reg(src), FixedReg(_)] => {
151-
fmtln!(f, "let {src} = self.{src}.enc();");
152-
f.add_block(&format!("match &self.{dst}"), |f| match src.bits() {
153-
128 => {
154-
fmtln!(
155-
f,
156-
"XmmMem::Xmm({dst}) => rex.emit_two_op(buf, {src}, {dst}.enc()),"
157-
);
158-
fmtln!(
159-
f,
160-
"XmmMem::Mem({dst}) => {dst}.emit_rex_prefix(rex, {src}, buf),"
161-
);
162-
}
163-
_ => {
164-
fmtln!(
165-
f,
166-
"GprMem::Gpr({dst}) => rex.emit_two_op(buf, {src}, {dst}.enc()),"
167-
);
168-
fmtln!(
169-
f,
170-
"GprMem::Mem({dst}) => {dst}.emit_rex_prefix(rex, {src}, buf),"
171-
);
172-
}
173-
});
116+
fmtln!(f, "let src = self.{src}.enc();");
117+
fmtln!(f, "let rex = self.{dst}.as_rex_prefix(src, {bits});");
174118
}
175-
176119
unknown => unimplemented!("unknown pattern: {unknown:?}"),
177120
}
121+
122+
fmtln!(f, "rex.encode(buf);");
178123
}
179124

180125
fn generate_modrm_byte(&self, f: &mut Formatter, rex: &dsl::Rex) {
@@ -271,13 +216,3 @@ impl dsl::Format {
271216
}
272217
}
273218
}
274-
275-
impl dsl::Rex {
276-
fn generate_flags(&self) -> &str {
277-
if self.w {
278-
"RexFlags::set_w()"
279-
} else {
280-
"RexFlags::clear_w()"
281-
}
282-
}
283-
}

cranelift/assembler-x64/src/gpr.rs

-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Pure register operands; see [`Gpr`].
22
3-
use crate::rex::RexFlags;
43
use crate::AsReg;
54

65
/// A general purpose x64 register (e.g., `%rax`).
@@ -33,12 +32,6 @@ impl<R: AsReg> Gpr<R> {
3332
pub fn to_string(&self, size: Size) -> String {
3433
self.0.to_string(Some(size))
3534
}
36-
37-
/// Proxy on the 8-bit REX flag emission; helpful for simplifying generated
38-
/// code.
39-
pub(crate) fn always_emit_if_8bit_needed(&self, rex: &mut RexFlags) {
40-
rex.always_emit_if_8bit_needed(self.enc());
41-
}
4235
}
4336

4437
impl<R: AsReg> AsRef<R> for Gpr<R> {

cranelift/assembler-x64/src/inst.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::api::{AsReg, CodeSink, KnownOffsetTable, RegisterVisitor, Registers};
77
use crate::gpr::{self, Gpr, Size};
88
use crate::imm::{Extension, Imm16, Imm32, Imm8, Simm32, Simm8};
99
use crate::mem::{emit_modrm_sib_disp, visit_amode, Amode, GprMem, XmmMem};
10-
use crate::rex::{self, RexFlags};
10+
use crate::rex::{self, RexPrefix};
1111
use crate::xmm::Xmm;
1212
use crate::Fixed;
1313

cranelift/assembler-x64/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,5 @@ pub use imm::{Extension, Imm16, Imm32, Imm8, Simm16, Simm32, Simm8};
8484
pub use mem::{
8585
Amode, AmodeOffset, AmodeOffsetPlusKnownOffset, DeferredTarget, GprMem, Scale, XmmMem,
8686
};
87-
pub use rex::RexFlags;
87+
pub use rex::RexPrefix;
8888
pub use xmm::Xmm;

cranelift/assembler-x64/src/mem.rs

+21-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::api::{AsReg, CodeSink, Constant, KnownOffset, KnownOffsetTable, Label, TrapCode};
44
use crate::gpr::{self, NonRspGpr, Size};
5-
use crate::rex::{encode_modrm, encode_sib, Imm, RexFlags};
5+
use crate::rex::{encode_modrm, encode_sib, Imm, RexPrefix};
66
use crate::{RegisterVisitor, Registers};
77

88
/// x64 memory addressing modes.
@@ -35,22 +35,17 @@ impl<R: AsReg> Amode<R> {
3535
}
3636
}
3737

38-
/// Encode the [`Amode`] into a ModRM/SIB/displacement sequence.
39-
pub fn emit_rex_prefix(&self, rex: RexFlags, enc_g: u8, sink: &mut impl CodeSink) {
38+
/// Return the [`RexPrefix`] for each variant of this [`Amode`].
39+
#[must_use]
40+
pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
4041
match self {
4142
Amode::ImmReg { base, .. } => {
42-
let enc_e = base.enc();
43-
rex.emit_two_op(sink, enc_g, enc_e);
43+
RexPrefix::two_op(enc_reg, base.enc(), has_w_bit, uses_8bit)
4444
}
4545
Amode::ImmRegRegShift { base, index, .. } => {
46-
let enc_base = base.enc();
47-
let enc_index = index.enc();
48-
rex.emit_three_op(sink, enc_g, enc_index, enc_base);
49-
}
50-
Amode::RipRelative { .. } => {
51-
// note REX.B = 0.
52-
rex.emit_two_op(sink, enc_g, 0);
46+
RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit)
5347
}
48+
Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit),
5449
}
5550
}
5651
}
@@ -265,14 +260,12 @@ impl<R: AsReg, M: AsReg> GprMem<R, M> {
265260
}
266261
}
267262

268-
/// Proxy on the 8-bit REX flag emission; helpful for simplifying generated
269-
/// code.
270-
pub(crate) fn always_emit_if_8bit_needed(&self, rex: &mut RexFlags) {
263+
/// Return the [`RexPrefix`] for each variant of this [`GprMem`].
264+
#[must_use]
265+
pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
271266
match self {
272-
GprMem::Gpr(gpr) => {
273-
rex.always_emit_if_8bit_needed(gpr.enc());
274-
}
275-
GprMem::Mem(_) => {}
267+
GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
268+
GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
276269
}
277270
}
278271
}
@@ -297,6 +290,15 @@ impl<R: AsReg, M: AsReg> XmmMem<R, M> {
297290
XmmMem::Mem(amode) => amode.to_string(),
298291
}
299292
}
293+
294+
/// Return the [`RexPrefix`] for each variant of this [`XmmMem`].
295+
#[must_use]
296+
pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
297+
match self {
298+
XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
299+
XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
300+
}
301+
}
300302
}
301303

302304
/// Emit the ModRM/SIB/displacement sequence for a memory operand.

0 commit comments

Comments
 (0)