Skip to content

Commit 65e50df

Browse files
committed
Extract VRegId from a usize
We would like to do type matching on the VRegId. Extracting the VRegID from a usize makes the code a bit easier to understand and refactor. MemBase uses a VReg, and there is also a VReg in Opnd. We should be sharing types between these two, so this is a step in the direction of sharing a type
1 parent 253bfd7 commit 65e50df

File tree

4 files changed

+75
-41
lines changed

4 files changed

+75
-41
lines changed

zjit/src/backend/arm64/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ impl Assembler {
564564
// Merge `csel` and `mov` into a single `csel` when possible
565565
match iterator.peek().map(|(_, insn)| insn) {
566566
Some(Insn::Mov { dest: Opnd::Reg(reg), src })
567-
if matches!(out, Opnd::VReg { .. }) && *out == *src && live_ranges[out.vreg_idx()].end() == index + 1 => {
567+
if matches!(out, Opnd::VReg { .. }) && *out == *src && live_ranges[out.vreg_idx().0].end() == index + 1 => {
568568
*out = Opnd::Reg(*reg);
569569
asm.push_insn(insn);
570570
iterator.next(asm); // Pop merged Insn::Mov
@@ -1702,7 +1702,7 @@ fn merge_three_reg_mov(
17021702
Opnd::Reg(_) | Opnd::VReg{..},
17031703
Some((mov_idx, Insn::Mov { dest, src })))
17041704
= (left, right, iterator.peek()) {
1705-
if out == src && live_ranges[out.vreg_idx()].end() == *mov_idx && matches!(*dest, Opnd::Reg(_) | Opnd::VReg{..}) {
1705+
if out == src && live_ranges[out.vreg_idx().0].end() == *mov_idx && matches!(*dest, Opnd::Reg(_) | Opnd::VReg{..}) {
17061706
*out = *dest;
17071707
iterator.next(asm); // Pop merged Insn::Mov
17081708
}

zjit/src/backend/lir.rs

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ use crate::state::rb_zjit_record_exit_stack;
2121
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
2222
pub struct BlockId(pub usize);
2323

24+
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
25+
pub struct VRegId(pub usize);
26+
2427
impl From<BlockId> for usize {
2528
fn from(val: BlockId) -> Self {
2629
val.0
@@ -131,7 +134,7 @@ pub enum MemBase
131134
/// Register: Every Opnd::Mem should have MemBase::Reg as of emit.
132135
Reg(u8),
133136
/// Virtual register: Lowered to MemBase::Reg or MemBase::Stack in alloc_regs.
134-
VReg(usize),
137+
VReg(VRegId),
135138
/// Stack slot: Lowered to MemBase::Reg in scratch_split.
136139
Stack { stack_idx: usize, num_bits: u8 },
137140
}
@@ -158,7 +161,7 @@ impl fmt::Display for Mem {
158161
write!(f, "[")?;
159162
match self.base {
160163
MemBase::Reg(reg_no) => write!(f, "{}", mem_base_reg(reg_no))?,
161-
MemBase::VReg(idx) => write!(f, "v{idx}")?,
164+
MemBase::VReg(idx) => write!(f, "v{}", idx.0)?,
162165
MemBase::Stack { stack_idx, num_bits } if num_bits == 64 => write!(f, "Stack[{stack_idx}]")?,
163166
MemBase::Stack { stack_idx, num_bits } => write!(f, "Stack{num_bits}[{stack_idx}]")?,
164167
}
@@ -196,7 +199,7 @@ pub enum Opnd
196199
Value(VALUE),
197200

198201
/// Virtual register. Lowered to Reg or Mem in Assembler::alloc_regs().
199-
VReg{ idx: usize, num_bits: u8 },
202+
VReg{ idx: VRegId, num_bits: u8 },
200203

201204
// Low-level operands, for lowering
202205
Imm(i64), // Raw signed immediate
@@ -212,8 +215,8 @@ impl fmt::Display for Opnd {
212215
None => write!(f, "None"),
213216
Value(VALUE(value)) if *value < 10 => write!(f, "Value({value:x})"),
214217
Value(VALUE(value)) => write!(f, "Value(0x{value:x})"),
215-
VReg { idx, num_bits } if *num_bits == 64 => write!(f, "v{idx}"),
216-
VReg { idx, num_bits } => write!(f, "VReg{num_bits}(v{idx})"),
218+
VReg { idx, num_bits } if *num_bits == 64 => write!(f, "v{}", idx.0),
219+
VReg { idx, num_bits } => write!(f, "VReg{num_bits}(v{})", idx.0),
217220
Imm(value) if value.abs() < 10 => write!(f, "Imm({value:x})"),
218221
Imm(value) => write!(f, "Imm(0x{value:x})"),
219222
UImm(value) if *value < 10 => write!(f, "{value:x}"),
@@ -230,8 +233,8 @@ impl fmt::Debug for Opnd {
230233
match self {
231234
Self::None => write!(fmt, "None"),
232235
Value(val) => write!(fmt, "Value({val:?})"),
233-
VReg { idx, num_bits } if *num_bits == 64 => write!(fmt, "VReg({idx})"),
234-
VReg { idx, num_bits } => write!(fmt, "VReg{num_bits}({idx})"),
236+
VReg { idx, num_bits } if *num_bits == 64 => write!(fmt, "VReg({})", idx.0),
237+
VReg { idx, num_bits } => write!(fmt, "VReg{num_bits}({})", idx.0),
235238
Imm(signed) => write!(fmt, "{signed:x}_i64"),
236239
UImm(unsigned) => write!(fmt, "{unsigned:x}_u64"),
237240
// Say Mem and Reg only once
@@ -282,7 +285,7 @@ impl Opnd
282285
}
283286

284287
/// Unwrap the index of a VReg
285-
pub fn vreg_idx(&self) -> usize {
288+
pub fn vreg_idx(&self) -> VRegId {
286289
match self {
287290
Opnd::VReg { idx, .. } => *idx,
288291
_ => unreachable!("trying to unwrap {self:?} into VReg"),
@@ -321,10 +324,10 @@ impl Opnd
321324
pub fn map_index(self, indices: &[usize]) -> Opnd {
322325
match self {
323326
Opnd::VReg { idx, num_bits } => {
324-
Opnd::VReg { idx: indices[idx], num_bits }
327+
Opnd::VReg { idx: VRegId(indices[idx.0]), num_bits }
325328
}
326329
Opnd::Mem(Mem { base: MemBase::VReg(idx), disp, num_bits }) => {
327-
Opnd::Mem(Mem { base: MemBase::VReg(indices[idx]), disp, num_bits })
330+
Opnd::Mem(Mem { base: MemBase::VReg(VRegId(indices[idx.0])), disp, num_bits })
328331
},
329332
_ => self
330333
}
@@ -1355,12 +1358,44 @@ impl LiveRange {
13551358
}
13561359
}
13571360

1361+
/// Type-safe wrapper around Vec<LiveRange> that can be indexed by VRegId
1362+
#[derive(Clone, Debug, Default)]
1363+
pub struct LiveRanges(Vec<LiveRange>);
1364+
1365+
impl std::ops::Index<VRegId> for LiveRanges {
1366+
type Output = LiveRange;
1367+
1368+
fn index(&self, idx: VRegId) -> &Self::Output {
1369+
&self.0[idx.0]
1370+
}
1371+
}
1372+
1373+
impl std::ops::IndexMut<VRegId> for LiveRanges {
1374+
fn index_mut(&mut self, idx: VRegId) -> &mut Self::Output {
1375+
&mut self.0[idx.0]
1376+
}
1377+
}
1378+
1379+
impl std::ops::Deref for LiveRanges {
1380+
type Target = Vec<LiveRange>;
1381+
1382+
fn deref(&self) -> &Self::Target {
1383+
&self.0
1384+
}
1385+
}
1386+
1387+
impl std::ops::DerefMut for LiveRanges {
1388+
fn deref_mut(&mut self) -> &mut Self::Target {
1389+
&mut self.0
1390+
}
1391+
}
1392+
13581393
/// StackState manages which stack slots are used by which VReg
13591394
pub struct StackState {
13601395
/// The maximum number of spilled VRegs at a time
13611396
stack_size: usize,
13621397
/// Map from index at the C stack for spilled VRegs to Some(vreg_idx) if allocated
1363-
stack_slots: Vec<Option<usize>>,
1398+
stack_slots: Vec<Option<VRegId>>,
13641399
/// Copy of Assembler::stack_base_idx. Used for calculating stack slot offsets.
13651400
stack_base_idx: usize,
13661401
}
@@ -1376,7 +1411,7 @@ impl StackState {
13761411
}
13771412

13781413
/// Allocate a stack slot for a given vreg_idx
1379-
fn alloc_stack(&mut self, vreg_idx: usize) -> Opnd {
1414+
fn alloc_stack(&mut self, vreg_idx: VRegId) -> Opnd {
13801415
for stack_idx in 0..self.stack_size {
13811416
if self.stack_slots[stack_idx].is_none() {
13821417
self.stack_slots[stack_idx] = Some(vreg_idx);
@@ -1437,7 +1472,7 @@ struct RegisterPool {
14371472

14381473
/// Some(vreg_idx) if the register at the index in `pool` is used by the VReg.
14391474
/// None if the register is not in use.
1440-
pool: Vec<Option<usize>>,
1475+
pool: Vec<Option<VRegId>>,
14411476

14421477
/// The number of live registers.
14431478
/// Provides a quick way to query `pool.filter(|r| r.is_some()).count()`
@@ -1461,7 +1496,7 @@ impl RegisterPool {
14611496

14621497
/// Mutate the pool to indicate that the register at the index
14631498
/// has been allocated and is live.
1464-
fn alloc_opnd(&mut self, vreg_idx: usize) -> Opnd {
1499+
fn alloc_opnd(&mut self, vreg_idx: VRegId) -> Opnd {
14651500
for (reg_idx, reg) in self.regs.iter().enumerate() {
14661501
if self.pool[reg_idx].is_none() {
14671502
self.pool[reg_idx] = Some(vreg_idx);
@@ -1473,7 +1508,7 @@ impl RegisterPool {
14731508
}
14741509

14751510
/// Allocate a specific register
1476-
fn take_reg(&mut self, reg: &Reg, vreg_idx: usize) -> Opnd {
1511+
fn take_reg(&mut self, reg: &Reg, vreg_idx: VRegId) -> Opnd {
14771512
let reg_idx = self.regs.iter().position(|elem| elem.reg_no == reg.reg_no)
14781513
.unwrap_or_else(|| panic!("Unable to find register: {}", reg.reg_no));
14791514
assert_eq!(self.pool[reg_idx], None, "register already allocated for VReg({:?})", self.pool[reg_idx]);
@@ -1499,7 +1534,7 @@ impl RegisterPool {
14991534
}
15001535

15011536
/// Return a list of (Reg, vreg_idx) tuples for all live registers
1502-
fn live_regs(&self) -> Vec<(Reg, usize)> {
1537+
fn live_regs(&self) -> Vec<(Reg, VRegId)> {
15031538
let mut live_regs = Vec::with_capacity(self.live_regs);
15041539
for (reg_idx, &reg) in self.regs.iter().enumerate() {
15051540
if let Some(vreg_idx) = self.pool[reg_idx] {
@@ -1510,7 +1545,7 @@ impl RegisterPool {
15101545
}
15111546

15121547
/// Return vreg_idx if a given register is already in use
1513-
fn vreg_for(&self, reg: &Reg) -> Option<usize> {
1548+
fn vreg_for(&self, reg: &Reg) -> Option<VRegId> {
15141549
let reg_idx = self.regs.iter().position(|elem| elem.reg_no == reg.reg_no).unwrap();
15151550
self.pool[reg_idx]
15161551
}
@@ -1536,7 +1571,7 @@ pub struct Assembler {
15361571
current_block_id: BlockId,
15371572

15381573
/// Live range for each VReg indexed by its `idx``
1539-
pub(super) live_ranges: Vec<LiveRange>,
1574+
pub(super) live_ranges: LiveRanges,
15401575

15411576
/// Names of labels
15421577
pub(super) label_names: Vec<String>,
@@ -1568,7 +1603,7 @@ impl Assembler
15681603
leaf_ccall_stack_size: None,
15691604
basic_blocks: Vec::default(),
15701605
current_block_id: BlockId(0),
1571-
live_ranges: Vec::default(),
1606+
live_ranges: LiveRanges::default(),
15721607
idx: 0,
15731608
}
15741609
}
@@ -1780,7 +1815,7 @@ impl Assembler
17801815

17811816
/// Build an Opnd::VReg and initialize its LiveRange
17821817
pub(super) fn new_vreg(&mut self, num_bits: u8) -> Opnd {
1783-
let vreg = Opnd::VReg { idx: self.live_ranges.len(), num_bits };
1818+
let vreg = Opnd::VReg { idx: VRegId(self.live_ranges.len()), num_bits };
17841819
self.live_ranges.push(LiveRange { start: None, end: None });
17851820
vreg
17861821
}
@@ -1794,7 +1829,7 @@ impl Assembler
17941829

17951830
// Initialize the live range of the output VReg to insn_idx..=insn_idx
17961831
if let Some(Opnd::VReg { idx, .. }) = insn.out_opnd() {
1797-
assert!(*idx < self.live_ranges.len());
1832+
assert!(idx.0 < self.live_ranges.len());
17981833
assert_eq!(self.live_ranges[*idx], LiveRange { start: None, end: None });
17991834
self.live_ranges[*idx] = LiveRange { start: Some(insn_idx), end: Some(insn_idx) };
18001835
}
@@ -1805,7 +1840,7 @@ impl Assembler
18051840
match *opnd {
18061841
Opnd::VReg { idx, .. } |
18071842
Opnd::Mem(Mem { base: MemBase::VReg(idx), .. }) => {
1808-
assert!(idx < self.live_ranges.len());
1843+
assert!(idx.0 < self.live_ranges.len());
18091844
assert_ne!(self.live_ranges[idx].end, None);
18101845
self.live_ranges[idx].end = Some(self.live_ranges[idx].end().max(insn_idx));
18111846
}
@@ -1894,7 +1929,7 @@ impl Assembler
18941929
let mut vreg_opnd: Vec<Option<Opnd>> = vec![None; self.live_ranges.len()];
18951930

18961931
// List of registers saved before a C call, paired with the VReg index.
1897-
let mut saved_regs: Vec<(Reg, usize)> = vec![];
1932+
let mut saved_regs: Vec<(Reg, VRegId)> = vec![];
18981933

18991934
// Remember the indexes of Insn::FrameSetup to update the stack size later
19001935
let mut frame_setup_idxs: Vec<(BlockId, usize)> = vec![];
@@ -1924,7 +1959,7 @@ impl Assembler
19241959
let new_opnd = pool.alloc_opnd(vreg_idx);
19251960
asm.mov(new_opnd, C_RET_OPND);
19261961
pool.dealloc_opnd(&Opnd::Reg(C_RET_REG));
1927-
vreg_opnd[vreg_idx] = Some(new_opnd);
1962+
vreg_opnd[vreg_idx.0] = Some(new_opnd);
19281963
}
19291964

19301965
true
@@ -1942,8 +1977,8 @@ impl Assembler
19421977
// We're going to check if this is the last instruction that
19431978
// uses this operand. If it is, we can return the allocated
19441979
// register to the pool.
1945-
if live_ranges[idx].end() == index {
1946-
if let Some(opnd) = vreg_opnd[idx] {
1980+
if live_ranges[idx.0].end() == index {
1981+
if let Some(opnd) = vreg_opnd[idx.0] {
19471982
pool.dealloc_opnd(&opnd);
19481983
} else {
19491984
unreachable!("no register allocated for insn {:?}", insn);
@@ -1986,8 +2021,8 @@ impl Assembler
19862021
_ => None,
19872022
};
19882023
if let Some(vreg_idx) = vreg_idx {
1989-
if live_ranges[vreg_idx].end() == index {
1990-
debug!("Allocating a register for VReg({}) at instruction index {} even though it does not live past this index", vreg_idx, index);
2024+
if live_ranges[vreg_idx.0].end() == index {
2025+
debug!("Allocating a register for VReg({}) at instruction index {} even though it does not live past this index", vreg_idx.0, index);
19912026
}
19922027
// This is going to be the output operand that we will set on the
19932028
// instruction. CCall and LiveReg need to use a specific register.
@@ -2011,8 +2046,8 @@ impl Assembler
20112046
let mut opnd_iter = insn.opnd_iter();
20122047

20132048
if let Some(Opnd::VReg{ idx, .. }) = opnd_iter.next() {
2014-
if live_ranges[*idx].end() == index {
2015-
if let Some(Opnd::Reg(reg)) = vreg_opnd[*idx] {
2049+
if live_ranges[idx.0].end() == index {
2050+
if let Some(Opnd::Reg(reg)) = vreg_opnd[idx.0] {
20162051
out_reg = Some(pool.take_reg(&reg, vreg_idx));
20172052
}
20182053
}
@@ -2031,7 +2066,7 @@ impl Assembler
20312066
// extends beyond the index of the instruction.
20322067
let out = insn.out_opnd_mut().unwrap();
20332068
let out_opnd = out_opnd.with_num_bits(out_num_bits);
2034-
vreg_opnd[out.vreg_idx()] = Some(out_opnd);
2069+
vreg_opnd[out.vreg_idx().0] = Some(out_opnd);
20352070
*out = out_opnd;
20362071
}
20372072

@@ -2040,10 +2075,10 @@ impl Assembler
20402075
while let Some(opnd) = opnd_iter.next() {
20412076
match *opnd {
20422077
Opnd::VReg { idx, num_bits } => {
2043-
*opnd = vreg_opnd[idx].unwrap().with_num_bits(num_bits);
2078+
*opnd = vreg_opnd[idx.0].unwrap().with_num_bits(num_bits);
20442079
},
20452080
Opnd::Mem(Mem { base: MemBase::VReg(idx), disp, num_bits }) => {
2046-
*opnd = match vreg_opnd[idx].unwrap() {
2081+
*opnd = match vreg_opnd[idx.0].unwrap() {
20472082
Opnd::Reg(reg) => Opnd::Mem(Mem { base: MemBase::Reg(reg.reg_no), disp, num_bits }),
20482083
// If the base is spilled, lower it to MemBase::Stack, which scratch_split will lower to MemBase::Reg.
20492084
Opnd::Mem(mem) => Opnd::Mem(Mem { base: pool.stack_state.mem_to_stack_membase(mem), disp, num_bits }),
@@ -2057,8 +2092,8 @@ impl Assembler
20572092
// If we have an output that dies at its definition (it is unused), free up the
20582093
// register
20592094
if let Some(idx) = vreg_idx {
2060-
if live_ranges[idx].end() == index {
2061-
if let Some(opnd) = vreg_opnd[idx] {
2095+
if live_ranges[idx.0].end() == index {
2096+
if let Some(opnd) = vreg_opnd[idx.0] {
20622097
pool.dealloc_opnd(&opnd);
20632098
} else {
20642099
unreachable!("no register allocated for insn {:?}", insn);

zjit/src/backend/tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::backend::lir::*;
33
use crate::cruby::*;
44
use crate::codegen::c_callable;
55
use crate::options::rb_zjit_prepare_options;
6-
use crate::hir;
76

87
#[test]
98
fn test_add() {

zjit/src/backend/x86_64/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl Assembler {
140140
{
141141
let mut asm_local = Assembler::new_with_asm(&self);
142142
let asm = &mut asm_local;
143-
let live_ranges: Vec<LiveRange> = take(&mut self.live_ranges);
143+
let live_ranges = take(&mut self.live_ranges);
144144
let mut iterator = self.instruction_iterator();
145145

146146
while let Some((index, mut insn)) = iterator.next(asm) {
@@ -166,9 +166,9 @@ impl Assembler {
166166
// When we split an operand, we can create a new VReg not in `live_ranges`.
167167
// So when we see a VReg with out-of-range index, it's created from splitting
168168
// from the loop above and we know it doesn't outlive the current instruction.
169-
let vreg_outlives_insn = |vreg_idx| {
169+
let vreg_outlives_insn = |vreg_idx: VRegId| {
170170
live_ranges
171-
.get(vreg_idx)
171+
.get(vreg_idx.0)
172172
.is_some_and(|live_range: &LiveRange| live_range.end() > index)
173173
};
174174

0 commit comments

Comments
 (0)