Skip to content

Commit 6ad0840

Browse files
authored
asm: allow printing of virtual registers (bytecodealliance#10389)
* Rename module: `reg` to `gpr` * Override `to_string` implementation for `cranelift-codegen` types * Add pretty-printing tests
1 parent c8f0787 commit 6ad0840

File tree

8 files changed

+103
-22
lines changed

8 files changed

+103
-22
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ impl dsl::Location {
7272
pub fn generate_fixed_reg(&self) -> Option<&str> {
7373
use dsl::Location::*;
7474
match self {
75-
al | ax | eax | rax => Some("reg::enc::RAX"),
76-
cl => Some("reg::enc::RCX"),
75+
al | ax | eax | rax => Some("gpr::enc::RAX"),
76+
cl => Some("gpr::enc::RCX"),
7777
imm8 | imm16 | imm32 | r8 | r16 | r32 | r64 | xmm | rm8 | rm16 | rm32 | rm64 | rm128 => None,
7878
}
7979
}

cranelift/assembler-x64/src/api.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Contains traits that a user of this assembler must implement.
22
3-
use crate::reg;
3+
use crate::gpr;
44
use crate::xmm;
55
use std::{num::NonZeroU8, ops::Index, vec::Vec};
66

@@ -130,10 +130,10 @@ pub trait AsReg: Clone + std::fmt::Debug {
130130
fn enc(&self) -> u8;
131131

132132
/// Return the register name.
133-
fn to_string(&self, size: Option<reg::Size>) -> &str {
133+
fn to_string(&self, size: Option<gpr::Size>) -> String {
134134
match size {
135-
Some(size) => reg::enc::to_string(self.enc(), size),
136-
None => xmm::enc::to_string(self.enc()),
135+
Some(size) => gpr::enc::to_string(self.enc(), size).into(),
136+
None => xmm::enc::to_string(self.enc()).into(),
137137
}
138138
}
139139
}

cranelift/assembler-x64/src/fuzz.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ impl Arbitrary<'_> for AmodeOffsetPlusKnownOffset {
198198
}
199199
impl<R: AsReg> Arbitrary<'_> for NonRspGpr<R> {
200200
fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {
201-
use crate::reg::enc::*;
201+
use crate::gpr::enc::*;
202202
let gpr = u.choose(&[
203203
RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15,
204204
])?;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ impl<R: AsReg> Gpr<R> {
3030
}
3131

3232
/// Return the register name at the given `size`.
33-
pub fn to_string(&self, size: Size) -> &str {
34-
enc::to_string(self.enc(), size)
33+
pub fn to_string(&self, size: Size) -> String {
34+
self.0.to_string(Some(size))
3535
}
3636

3737
/// Proxy on the 8-bit REX flag emission; helpful for simplifying generated

cranelift/assembler-x64/src/inst.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
//! See also: [`Inst`], an `enum` containing all these instructions.
55
66
use crate::api::{AsReg, CodeSink, KnownOffsetTable, RegisterVisitor, Registers};
7+
use crate::gpr::{self, Gpr, Size};
78
use crate::imm::{Extension, Imm16, Imm32, Imm8, Simm32, Simm8};
89
use crate::mem::{emit_modrm_sib_disp, GprMem, XmmMem};
9-
use crate::reg::{self, Gpr, Size};
1010
use crate::rex::{self, RexFlags};
1111
use crate::xmm::Xmm;
1212

cranelift/assembler-x64/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@
4444
)]
4545

4646
mod api;
47+
pub mod gpr;
4748
mod imm;
4849
pub mod inst;
4950
mod mem;
50-
mod reg;
5151
mod rex;
52-
mod xmm;
52+
pub mod xmm;
5353

5454
#[cfg(any(test, feature = "fuzz"))]
5555
pub mod fuzz;
@@ -76,11 +76,11 @@ pub use api::{
7676
AsReg, CodeSink, Constant, KnownOffset, KnownOffsetTable, Label, RegisterVisitor, Registers,
7777
TrapCode,
7878
};
79+
pub use gpr::{Gpr, NonRspGpr, Size};
7980
pub use imm::{Extension, Imm16, Imm32, Imm8, Simm16, Simm32, Simm8};
8081
pub use mem::{
8182
Amode, AmodeOffset, AmodeOffsetPlusKnownOffset, DeferredTarget, GprMem, Scale, XmmMem,
8283
};
83-
pub use reg::{Gpr, NonRspGpr, Size};
8484
pub use rex::RexFlags;
8585
pub use xmm::Xmm;
8686

cranelift/assembler-x64/src/mem.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Memory operands to instructions.
22
33
use crate::api::{AsReg, CodeSink, Constant, KnownOffset, KnownOffsetTable, Label, TrapCode};
4-
use crate::reg::{self, NonRspGpr, Size};
4+
use crate::gpr::{self, NonRspGpr, Size};
55
use crate::rex::{encode_modrm, encode_sib, Imm, RexFlags};
66
use crate::xmm;
77

@@ -169,7 +169,7 @@ impl<R: AsReg> std::fmt::Display for Amode<R> {
169169
Amode::ImmReg { simm32, base, .. } => {
170170
// Note: size is always 8; the address is 64 bits,
171171
// even if the addressed operand is smaller.
172-
let base = reg::enc::to_string(base.enc(), Size::Quadword);
172+
let base = gpr::enc::to_string(base.enc(), Size::Quadword);
173173
write!(f, "{simm32:x}({base})")
174174
}
175175
Amode::ImmRegRegShift {
@@ -179,8 +179,8 @@ impl<R: AsReg> std::fmt::Display for Amode<R> {
179179
scale,
180180
..
181181
} => {
182-
let base = reg::enc::to_string(base.enc(), Size::Quadword);
183-
let index = reg::enc::to_string(index.enc(), Size::Quadword);
182+
let base = gpr::enc::to_string(base.enc(), Size::Quadword);
183+
let index = gpr::enc::to_string(index.enc(), Size::Quadword);
184184
let shift = scale.shift();
185185
if shift > 1 {
186186
write!(f, "{simm32:x}({base}, {index}, {shift})")
@@ -256,7 +256,7 @@ impl<R: AsReg, M: AsReg> GprMem<R, M> {
256256
/// Pretty-print the operand.
257257
pub fn to_string(&self, size: Size) -> String {
258258
match self {
259-
GprMem::Gpr(gpr) => reg::enc::to_string(gpr.enc(), size).to_owned(),
259+
GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
260260
GprMem::Mem(amode) => amode.to_string(),
261261
}
262262
}
@@ -313,7 +313,7 @@ pub fn emit_modrm_sib_disp<R: AsReg>(
313313
// optional immediate. If rsp is the base register, however, then a
314314
// SIB byte must be used.
315315
let enc_e_low3 = enc_e & 7;
316-
if enc_e_low3 == reg::enc::RSP {
316+
if enc_e_low3 == gpr::enc::RSP {
317317
// Displacement from RSP is encoded with a SIB byte where
318318
// the index and base are both encoded as RSP's encoding of
319319
// 0b100. This special encoding means that the index register
@@ -326,7 +326,7 @@ pub fn emit_modrm_sib_disp<R: AsReg>(
326326
// If the base register is rbp and there's no offset then force
327327
// a 1-byte zero offset since otherwise the encoding would be
328328
// invalid.
329-
if enc_e_low3 == reg::enc::RBP {
329+
if enc_e_low3 == gpr::enc::RBP {
330330
imm.force_immediate();
331331
}
332332
sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
@@ -348,14 +348,14 @@ pub fn emit_modrm_sib_disp<R: AsReg>(
348348
// ever be rsp. Note, though, that the encoding of r12, whose three
349349
// lower bits match the encoding of rsp, is explicitly allowed with
350350
// REX bytes so only rsp is disallowed.
351-
assert!(enc_index != reg::enc::RSP);
351+
assert!(enc_index != gpr::enc::RSP);
352352

353353
// If the offset is zero then there is no immediate. Note, though,
354354
// that if the base register's lower three bits are `101` then an
355355
// offset must be present. This is a special case in the encoding of
356356
// the SIB byte and requires an explicit displacement with rbp/r13.
357357
let mut imm = Imm::new(simm32.value(), evex_scaling);
358-
if enc_base & 7 == reg::enc::RBP {
358+
if enc_base & 7 == gpr::enc::RBP {
359359
imm.force_immediate();
360360
}
361361

cranelift/codegen/src/isa/x64/inst/external.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use super::{
66
};
77
use crate::ir::TrapCode;
88
use cranelift_assembler_x64 as asm;
9+
use std::string::String;
910

1011
/// Define the types of registers Cranelift will use.
1112
#[derive(Clone, Debug)]
@@ -38,6 +39,16 @@ impl asm::AsReg for PairedGpr {
3839
write
3940
}
4041

42+
fn to_string(&self, size: Option<asm::Size>) -> String {
43+
if self.read.is_real() {
44+
asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
45+
} else {
46+
let read = self.read.to_reg();
47+
let write = self.write.to_reg().to_reg();
48+
format!("(%{write:?} <- %{read:?})")
49+
}
50+
}
51+
4152
fn new(_: u8) -> Self {
4253
panic!("disallow creation of new assembler registers")
4354
}
@@ -59,6 +70,17 @@ impl asm::AsReg for PairedXmm {
5970
write
6071
}
6172

73+
fn to_string(&self, size: Option<asm::Size>) -> String {
74+
assert!(size.is_none(), "XMM registers do not have size variants");
75+
if self.read.is_real() {
76+
asm::xmm::enc::to_string(self.enc()).into()
77+
} else {
78+
let read = self.read.to_reg();
79+
let write = self.write.to_reg().to_reg();
80+
format!("(%{write:?} <- %{read:?})")
81+
}
82+
}
83+
6284
fn new(_: u8) -> Self {
6385
panic!("disallow creation of new assembler registers")
6486
}
@@ -70,6 +92,14 @@ impl asm::AsReg for Gpr {
7092
enc_gpr(self)
7193
}
7294

95+
fn to_string(&self, size: Option<asm::Size>) -> String {
96+
if self.is_real() {
97+
asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
98+
} else {
99+
format!("%{:?}", self.to_reg())
100+
}
101+
}
102+
73103
fn new(_: u8) -> Self {
74104
panic!("disallow creation of new assembler registers")
75105
}
@@ -81,6 +111,15 @@ impl asm::AsReg for Xmm {
81111
enc_xmm(self)
82112
}
83113

114+
fn to_string(&self, size: Option<asm::Size>) -> String {
115+
assert!(size.is_none(), "XMM registers do not have size variants");
116+
if self.is_real() {
117+
asm::xmm::enc::to_string(self.enc()).into()
118+
} else {
119+
format!("%{:?}", self.to_reg())
120+
}
121+
}
122+
84123
fn new(_: u8) -> Self {
85124
panic!("disallow creation of new assembler registers")
86125
}
@@ -286,3 +325,45 @@ impl From<asm::Constant> for VCodeConstant {
286325
// the assembler instructions exposed to ISLE.
287326
include!(concat!(env!("OUT_DIR"), "/assembler-isle-macro.rs"));
288327
pub(crate) use isle_assembler_methods;
328+
329+
#[cfg(test)]
330+
mod tests {
331+
use super::asm::{AsReg, Size};
332+
use super::PairedGpr;
333+
use crate::isa::x64::args::{FromWritableReg, Gpr, WritableGpr, WritableXmm, Xmm};
334+
use crate::isa::x64::inst::external::PairedXmm;
335+
use crate::{Reg, Writable};
336+
use regalloc2::{RegClass, VReg};
337+
338+
#[test]
339+
fn pretty_print_registers() {
340+
// For logging, we need to be able to pretty-print the virtual registers
341+
// that Cranelift uses before register allocation. This test ensures
342+
// that these remain printable using the `AsReg::to_string` interface
343+
// (see issue #10631).
344+
345+
let v200: Reg = VReg::new(200, RegClass::Int).into();
346+
let gpr200 = Gpr::new(v200).unwrap();
347+
assert_eq!(gpr200.to_string(Some(Size::Quadword)), "%v200");
348+
349+
let v300: Reg = VReg::new(300, RegClass::Int).into();
350+
let wgpr300 = WritableGpr::from_writable_reg(Writable::from_reg(v300).into()).unwrap();
351+
let pair = PairedGpr {
352+
read: gpr200,
353+
write: wgpr300,
354+
};
355+
assert_eq!(pair.to_string(Some(Size::Quadword)), "(%v300 <- %v200)");
356+
357+
let v400: Reg = VReg::new(400, RegClass::Float).into();
358+
let xmm400 = Xmm::new(v400).unwrap();
359+
assert_eq!(xmm400.to_string(None), "%v400");
360+
361+
let v500: Reg = VReg::new(500, RegClass::Float).into();
362+
let wxmm500 = WritableXmm::from_writable_reg(Writable::from_reg(v500).into()).unwrap();
363+
let pair = PairedXmm {
364+
read: xmm400,
365+
write: wxmm500,
366+
};
367+
assert_eq!(pair.to_string(None), "(%v500 <- %v400)");
368+
}
369+
}

0 commit comments

Comments
 (0)