Skip to content

Commit 9e1be64

Browse files
authored
x64: use ByteSink more liberally in the rex module (#9505)
* x64: use `ByteSink` more liberally in the `rex` module In looking at adding auto-generated assembler code to Cranelift, I've noticed that we pass `MachBuffer` down when it is not always necessary. The `ByteSink` trait (which `MachBuffer` implements) is a simplified view to `put*` bytes into the buffer and it is a bit simpler to use when testing since it is also implemented by `Vec<u8>`. This change propagates the trait to the `rex` module. * review: remove unused code
1 parent 15157c4 commit 9e1be64

File tree

1 file changed

+37
-39
lines changed
  • cranelift/codegen/src/isa/x64/encoding

1 file changed

+37
-39
lines changed

cranelift/codegen/src/isa/x64/encoding/rex.rs

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
//! Encodes instructions in the standard x86 encoding mode. This is called IA-32E mode in the Intel
2-
//! manuals but corresponds to the addition of the REX-prefix format (hence the name of this module)
3-
//! that allowed encoding instructions in both compatibility mode (32-bit instructions running on a
1+
//! Encodes instructions in the standard x86 encoding mode. This is called
2+
//! IA-32E mode in the Intel manuals but corresponds to the addition of the
3+
//! REX-prefix format (hence the name of this module) that allowed encoding
4+
//! instructions in both compatibility mode (32-bit instructions running on a
45
//! 64-bit OS) and in 64-bit mode (using the full 64-bit address space).
56
//!
6-
//! For all of the routines that take both a memory-or-reg operand (sometimes called "E" in the
7-
//! Intel documentation, see the Intel Developer's manual, vol. 2, section A.2) and a reg-only
8-
//! operand ("G" in Intelese), the order is always G first, then E. The term "enc" in the following
9-
//! means "hardware register encoding number".
10-
11-
use crate::machinst::{Reg, RegClass};
12-
use crate::{
13-
isa::x64::inst::{
14-
args::{Amode, OperandSize},
15-
regs, Inst, LabelUse,
16-
},
17-
machinst::MachBuffer,
18-
};
7+
//! For all of the routines that take both a memory-or-reg operand (sometimes
8+
//! called "E" in the Intel documentation, see the Intel Developer's manual,
9+
//! vol. 2, section A.2) and a reg-only operand ("G" in Intel-ese), the order is
10+
//! always G first, then E. The term "enc" in the following means "hardware
11+
//! register encoding number".
12+
13+
use super::ByteSink;
14+
use crate::isa::x64::inst::args::{Amode, OperandSize};
15+
use crate::isa::x64::inst::{regs, Inst, LabelUse};
16+
use crate::machinst::{MachBuffer, Reg, RegClass};
1917

2018
pub(crate) fn low8_will_sign_extend_to_64(x: u32) -> bool {
2119
let xs = (x as i32) as i64;
@@ -81,13 +79,25 @@ impl RexFlags {
8179
Self(1)
8280
}
8381

82+
/// True if 64-bit operands are used.
83+
#[inline(always)]
84+
pub fn must_clear_w(&self) -> bool {
85+
(self.0 & 1) != 0
86+
}
87+
8488
/// Require that the REX prefix is emitted.
8589
#[inline(always)]
8690
pub fn always_emit(&mut self) -> &mut Self {
8791
self.0 = self.0 | 2;
8892
self
8993
}
9094

95+
/// True if the REX prefix must always be emitted.
96+
#[inline(always)]
97+
pub fn must_always_emit(&self) -> bool {
98+
(self.0 & 2) != 0
99+
}
100+
91101
/// Emit the rex prefix if the referenced register would require it for 8-bit operations.
92102
#[inline(always)]
93103
pub fn always_emit_if_8bit_needed(&mut self, reg: Reg) -> &mut Self {
@@ -98,21 +108,9 @@ impl RexFlags {
98108
self
99109
}
100110

101-
/// True if 64-bit operands are used.
102-
#[inline(always)]
103-
pub fn must_clear_w(&self) -> bool {
104-
(self.0 & 1) != 0
105-
}
106-
107-
/// True if the REX prefix must always be emitted.
108-
#[inline(always)]
109-
pub fn must_always_emit(&self) -> bool {
110-
(self.0 & 2) != 0
111-
}
112-
113111
/// Emit a unary instruction.
114112
#[inline(always)]
115-
pub fn emit_one_op(&self, sink: &mut MachBuffer<Inst>, enc_e: u8) {
113+
pub fn emit_one_op<BS: ByteSink + ?Sized>(&self, sink: &mut BS, enc_e: u8) {
116114
// Register Operand coded in Opcode Byte
117115
// REX.R and REX.X unused
118116
// REX.B == 1 accesses r8-r15
@@ -128,7 +126,7 @@ impl RexFlags {
128126

129127
/// Emit a binary instruction.
130128
#[inline(always)]
131-
pub fn emit_two_op(&self, sink: &mut MachBuffer<Inst>, enc_g: u8, enc_e: u8) {
129+
pub fn emit_two_op<BS: ByteSink + ?Sized>(&self, sink: &mut BS, enc_g: u8, enc_e: u8) {
132130
let w = if self.must_clear_w() { 0 } else { 1 };
133131
let r = (enc_g >> 3) & 1;
134132
let x = 0;
@@ -141,9 +139,9 @@ impl RexFlags {
141139

142140
/// Emit a ternary instruction.
143141
#[inline(always)]
144-
pub fn emit_three_op(
142+
pub fn emit_three_op<BS: ByteSink + ?Sized>(
145143
&self,
146-
sink: &mut MachBuffer<Inst>,
144+
sink: &mut BS,
147145
enc_g: u8,
148146
enc_index: u8,
149147
enc_base: u8,
@@ -232,7 +230,7 @@ pub enum LegacyPrefixes {
232230
impl LegacyPrefixes {
233231
/// Emit the legacy prefix as bytes (e.g. in REX instructions).
234232
#[inline(always)]
235-
pub(crate) fn emit(&self, sink: &mut MachBuffer<Inst>) {
233+
pub(crate) fn emit<BS: ByteSink + ?Sized>(&self, sink: &mut BS) {
236234
match self {
237235
Self::_66 => sink.put1(0x66),
238236
Self::_F0 => sink.put1(0xF0),
@@ -501,7 +499,7 @@ impl Imm {
501499
}
502500
}
503501

504-
fn emit(&self, sink: &mut MachBuffer<Inst>) {
502+
fn emit<BS: ByteSink + ?Sized>(&self, sink: &mut BS) {
505503
match self {
506504
Imm::None => {}
507505
Imm::Imm8(n) => sink.put1(*n as u8),
@@ -514,8 +512,8 @@ impl Imm {
514512
///
515513
/// This is conceptually the same as emit_modrm_sib_enc_ge, except it is for the case where the E
516514
/// operand is a register rather than memory. Hence it is much simpler.
517-
pub(crate) fn emit_std_enc_enc(
518-
sink: &mut MachBuffer<Inst>,
515+
pub(crate) fn emit_std_enc_enc<BS: ByteSink + ?Sized>(
516+
sink: &mut BS,
519517
prefixes: LegacyPrefixes,
520518
opcodes: u32,
521519
mut num_opcodes: usize,
@@ -571,8 +569,8 @@ pub(crate) fn emit_std_reg_mem(
571569
);
572570
}
573571

574-
pub(crate) fn emit_std_reg_reg(
575-
sink: &mut MachBuffer<Inst>,
572+
pub(crate) fn emit_std_reg_reg<BS: ByteSink + ?Sized>(
573+
sink: &mut BS,
576574
prefixes: LegacyPrefixes,
577575
opcodes: u32,
578576
num_opcodes: usize,
@@ -586,7 +584,7 @@ pub(crate) fn emit_std_reg_reg(
586584
}
587585

588586
/// Write a suitable number of bits from an imm64 to the sink.
589-
pub(crate) fn emit_simm(sink: &mut MachBuffer<Inst>, size: u8, simm32: u32) {
587+
pub(crate) fn emit_simm<BS: ByteSink + ?Sized>(sink: &mut BS, size: u8, simm32: u32) {
590588
match size {
591589
8 | 4 => sink.put4(simm32),
592590
2 => sink.put2(simm32 as u16),

0 commit comments

Comments
 (0)