Skip to content

Commit d9a7a29

Browse files
committed
Add register repacking
1 parent 1735e79 commit d9a7a29

5 files changed

Lines changed: 257 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
code using `anyhow` (or similar) may not need to change
3434
- Fix a bug in bulk evaluator argument checks where mismatched slices could be
3535
allowed under some circumstances
36+
- Add `VmData::asm` to get an immutable reference to the inner `RegTape`
37+
- Add `RegTape::repack_map` and `RegTape::repack` to repack registers by
38+
frequency (making register 0 the most frequently used, etc)
39+
- Add `RegOp::visit_regs` and `RegOp::visit_regs_mut` to visit registers in an
40+
operation
3641

3742
# 0.4.3
3843
- Fixed bug in x86 interval `OR` function ([#395](https://github.com/mkeeter/fidget/pull/395)),

fidget-bytecode/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ impl Bytecode {
193193
pub fn new<const N: usize>(
194194
t: &VmData<N>,
195195
) -> Result<Self, ReservedRegister> {
196+
// Build a map for repacking registers by frequency
197+
let map = t.asm().repack_map();
196198
// The initial opcode is `OP_JUMP 0x0000_0000`
197199
let mut data = vec![u32::MAX, 0u32];
198200
let mut reg_count = 0u8;
@@ -205,6 +207,7 @@ impl Bytecode {
205207
if r == u8::MAX {
206208
Err(ReservedRegister)
207209
} else {
210+
let r = map[&r];
208211
reg_count = reg_count.max(r + 1);
209212
word[i] = r;
210213
Ok(())

fidget-core/src/compiler/op.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,214 @@ opcodes!(
288288
Store(u8, u32),
289289
}
290290
);
291+
292+
impl RegOp {
293+
/// Returns the output register (if present)
294+
pub fn output(&self) -> Option<u8> {
295+
match self {
296+
RegOp::Input(out, ..)
297+
| RegOp::CopyImm(out, ..)
298+
| RegOp::NegReg(out, ..)
299+
| RegOp::AbsReg(out, ..)
300+
| RegOp::RecipReg(out, ..)
301+
| RegOp::SqrtReg(out, ..)
302+
| RegOp::SquareReg(out, ..)
303+
| RegOp::FloorReg(out, ..)
304+
| RegOp::CeilReg(out, ..)
305+
| RegOp::RoundReg(out, ..)
306+
| RegOp::CopyReg(out, ..)
307+
| RegOp::SinReg(out, ..)
308+
| RegOp::CosReg(out, ..)
309+
| RegOp::TanReg(out, ..)
310+
| RegOp::AsinReg(out, ..)
311+
| RegOp::AcosReg(out, ..)
312+
| RegOp::AtanReg(out, ..)
313+
| RegOp::ExpReg(out, ..)
314+
| RegOp::LnReg(out, ..)
315+
| RegOp::NotReg(out, ..)
316+
| RegOp::AddRegImm(out, ..)
317+
| RegOp::MulRegImm(out, ..)
318+
| RegOp::DivRegImm(out, ..)
319+
| RegOp::DivImmReg(out, ..)
320+
| RegOp::SubImmReg(out, ..)
321+
| RegOp::SubRegImm(out, ..)
322+
| RegOp::AddRegReg(out, ..)
323+
| RegOp::MulRegReg(out, ..)
324+
| RegOp::DivRegReg(out, ..)
325+
| RegOp::SubRegReg(out, ..)
326+
| RegOp::AtanRegReg(out, ..)
327+
| RegOp::AtanRegImm(out, ..)
328+
| RegOp::AtanImmReg(out, ..)
329+
| RegOp::MinRegImm(out, ..)
330+
| RegOp::MaxRegImm(out, ..)
331+
| RegOp::MinRegReg(out, ..)
332+
| RegOp::MaxRegReg(out, ..)
333+
| RegOp::CompareRegReg(out, ..)
334+
| RegOp::CompareRegImm(out, ..)
335+
| RegOp::CompareImmReg(out, ..)
336+
| RegOp::ModRegReg(out, ..)
337+
| RegOp::ModRegImm(out, ..)
338+
| RegOp::ModImmReg(out, ..)
339+
| RegOp::AndRegImm(out, ..)
340+
| RegOp::AndRegReg(out, ..)
341+
| RegOp::OrRegImm(out, ..)
342+
| RegOp::OrRegReg(out, ..)
343+
| RegOp::Load(out, ..) => Some(*out),
344+
RegOp::Output(..) | RegOp::Store(..) => None,
345+
}
346+
}
347+
348+
/// Apply a mutating function to every register in the op
349+
///
350+
/// Both inputs and outputs are visited
351+
pub fn visit_regs_mut<F: FnMut(&mut u8)>(&mut self, mut f: F) {
352+
match self {
353+
RegOp::CopyImm(out, imm) => {
354+
let _: f32 = *imm;
355+
f(out)
356+
}
357+
RegOp::NegReg(out, arg)
358+
| RegOp::AbsReg(out, arg)
359+
| RegOp::RecipReg(out, arg)
360+
| RegOp::SqrtReg(out, arg)
361+
| RegOp::SquareReg(out, arg)
362+
| RegOp::FloorReg(out, arg)
363+
| RegOp::CeilReg(out, arg)
364+
| RegOp::RoundReg(out, arg)
365+
| RegOp::CopyReg(out, arg)
366+
| RegOp::SinReg(out, arg)
367+
| RegOp::CosReg(out, arg)
368+
| RegOp::TanReg(out, arg)
369+
| RegOp::AsinReg(out, arg)
370+
| RegOp::AcosReg(out, arg)
371+
| RegOp::AtanReg(out, arg)
372+
| RegOp::ExpReg(out, arg)
373+
| RegOp::LnReg(out, arg)
374+
| RegOp::NotReg(out, arg) => {
375+
f(out);
376+
f(arg);
377+
}
378+
RegOp::AddRegImm(out, arg, imm)
379+
| RegOp::MulRegImm(out, arg, imm)
380+
| RegOp::DivRegImm(out, arg, imm)
381+
| RegOp::DivImmReg(out, arg, imm)
382+
| RegOp::SubImmReg(out, arg, imm)
383+
| RegOp::SubRegImm(out, arg, imm)
384+
| RegOp::AtanRegImm(out, arg, imm)
385+
| RegOp::AtanImmReg(out, arg, imm)
386+
| RegOp::MinRegImm(out, arg, imm)
387+
| RegOp::MaxRegImm(out, arg, imm)
388+
| RegOp::CompareRegImm(out, arg, imm)
389+
| RegOp::CompareImmReg(out, arg, imm)
390+
| RegOp::ModRegImm(out, arg, imm)
391+
| RegOp::ModImmReg(out, arg, imm)
392+
| RegOp::AndRegImm(out, arg, imm)
393+
| RegOp::OrRegImm(out, arg, imm) => {
394+
let _: f32 = *imm; // type-checking pattern
395+
f(out);
396+
f(arg);
397+
}
398+
399+
RegOp::AddRegReg(out, lhs, rhs)
400+
| RegOp::MulRegReg(out, lhs, rhs)
401+
| RegOp::DivRegReg(out, lhs, rhs)
402+
| RegOp::SubRegReg(out, lhs, rhs)
403+
| RegOp::AtanRegReg(out, lhs, rhs)
404+
| RegOp::MinRegReg(out, lhs, rhs)
405+
| RegOp::MaxRegReg(out, lhs, rhs)
406+
| RegOp::CompareRegReg(out, lhs, rhs)
407+
| RegOp::ModRegReg(out, lhs, rhs)
408+
| RegOp::AndRegReg(out, lhs, rhs)
409+
| RegOp::OrRegReg(out, lhs, rhs) => {
410+
f(out);
411+
f(lhs);
412+
f(rhs);
413+
}
414+
415+
RegOp::Output(reg, imm)
416+
| RegOp::Input(reg, imm)
417+
| RegOp::Store(reg, imm)
418+
| RegOp::Load(reg, imm) => {
419+
let _: u32 = *imm; // type-checking pattern
420+
f(reg)
421+
}
422+
}
423+
}
424+
425+
/// Apply a function to every register in the op
426+
///
427+
/// Both inputs and outputs are visited
428+
pub fn visit_regs<F: FnMut(u8)>(&self, mut f: F) {
429+
match self {
430+
RegOp::CopyImm(out, imm) => {
431+
let _: f32 = *imm;
432+
f(*out)
433+
}
434+
RegOp::NegReg(out, arg)
435+
| RegOp::AbsReg(out, arg)
436+
| RegOp::RecipReg(out, arg)
437+
| RegOp::SqrtReg(out, arg)
438+
| RegOp::SquareReg(out, arg)
439+
| RegOp::FloorReg(out, arg)
440+
| RegOp::CeilReg(out, arg)
441+
| RegOp::RoundReg(out, arg)
442+
| RegOp::CopyReg(out, arg)
443+
| RegOp::SinReg(out, arg)
444+
| RegOp::CosReg(out, arg)
445+
| RegOp::TanReg(out, arg)
446+
| RegOp::AsinReg(out, arg)
447+
| RegOp::AcosReg(out, arg)
448+
| RegOp::AtanReg(out, arg)
449+
| RegOp::ExpReg(out, arg)
450+
| RegOp::LnReg(out, arg)
451+
| RegOp::NotReg(out, arg) => {
452+
f(*out);
453+
f(*arg);
454+
}
455+
RegOp::AddRegImm(out, arg, imm)
456+
| RegOp::MulRegImm(out, arg, imm)
457+
| RegOp::DivRegImm(out, arg, imm)
458+
| RegOp::DivImmReg(out, arg, imm)
459+
| RegOp::SubImmReg(out, arg, imm)
460+
| RegOp::SubRegImm(out, arg, imm)
461+
| RegOp::AtanRegImm(out, arg, imm)
462+
| RegOp::AtanImmReg(out, arg, imm)
463+
| RegOp::MinRegImm(out, arg, imm)
464+
| RegOp::MaxRegImm(out, arg, imm)
465+
| RegOp::CompareRegImm(out, arg, imm)
466+
| RegOp::CompareImmReg(out, arg, imm)
467+
| RegOp::ModRegImm(out, arg, imm)
468+
| RegOp::ModImmReg(out, arg, imm)
469+
| RegOp::AndRegImm(out, arg, imm)
470+
| RegOp::OrRegImm(out, arg, imm) => {
471+
let _: f32 = *imm; // type-checking pattern
472+
f(*out);
473+
f(*arg);
474+
}
475+
476+
RegOp::AddRegReg(out, lhs, rhs)
477+
| RegOp::MulRegReg(out, lhs, rhs)
478+
| RegOp::DivRegReg(out, lhs, rhs)
479+
| RegOp::SubRegReg(out, lhs, rhs)
480+
| RegOp::AtanRegReg(out, lhs, rhs)
481+
| RegOp::MinRegReg(out, lhs, rhs)
482+
| RegOp::MaxRegReg(out, lhs, rhs)
483+
| RegOp::CompareRegReg(out, lhs, rhs)
484+
| RegOp::ModRegReg(out, lhs, rhs)
485+
| RegOp::AndRegReg(out, lhs, rhs)
486+
| RegOp::OrRegReg(out, lhs, rhs) => {
487+
f(*out);
488+
f(*lhs);
489+
f(*rhs);
490+
}
491+
492+
RegOp::Output(reg, imm)
493+
| RegOp::Input(reg, imm)
494+
| RegOp::Store(reg, imm)
495+
| RegOp::Load(reg, imm) => {
496+
let _: u32 = *imm; // type-checking pattern
497+
f(*reg)
498+
}
499+
}
500+
}
501+
}

fidget-core/src/compiler/reg_tape.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Tape used for evaluation
22
use crate::compiler::{RegOp, RegisterAllocator, SsaTape};
33
use serde::{Deserialize, Serialize};
4+
use std::collections::HashMap;
45

56
/// Low-level tape for use with the Fidget virtual machine (or to be lowered
67
/// further into machine instructions).
@@ -9,6 +10,9 @@ pub struct RegTape {
910
tape: Vec<RegOp>,
1011

1112
/// Total allocated slots
13+
///
14+
/// This is a continuous space of registers (`0..N`) and memory (`N..`),
15+
/// where `N` is the parameter in [`RegTape::new`].
1216
pub(super) slot_count: u32,
1317
}
1418

@@ -27,6 +31,35 @@ impl RegTape {
2731
alloc.finalize()
2832
}
2933

34+
/// Repacks registers by frequency (so that register 0 is the most frequent)
35+
pub fn repack(&mut self) {
36+
let map = self.repack_map();
37+
for op in &mut self.tape {
38+
op.visit_regs_mut(|reg| *reg = map[reg]);
39+
}
40+
}
41+
42+
/// Returns a map for register repacking
43+
///
44+
/// The map repacks registers in the tape by frequency, so that register 0
45+
/// is the most frequent.
46+
pub fn repack_map(&self) -> HashMap<u8, u8> {
47+
let mut reg_counts: HashMap<u8, usize> = HashMap::new();
48+
for op in &self.tape {
49+
op.visit_regs(|reg| *reg_counts.entry(reg).or_default() += 1);
50+
}
51+
let mut sorted = reg_counts
52+
.into_iter()
53+
.map(|(reg, count)| (std::cmp::Reverse(count), reg))
54+
.collect::<Vec<_>>();
55+
sorted.sort_unstable();
56+
sorted
57+
.into_iter()
58+
.enumerate()
59+
.map(|(i, (_count, reg))| (reg, u8::try_from(i).unwrap()))
60+
.collect()
61+
}
62+
3063
/// Builds a new empty tape
3164
pub(crate) fn empty() -> Self {
3265
Self {

fidget-core/src/vm/data.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ impl<const N: usize> VmData<N> {
318318
self.asm.iter().cloned().rev()
319319
}
320320

321+
/// Returns a reference to the inner [`RegTape`]
322+
pub fn asm(&self) -> &RegTape {
323+
&self.asm
324+
}
325+
321326
/// Pretty-prints the inner SSA tape
322327
pub fn pretty_print(&self) {
323328
self.ssa.pretty_print();

0 commit comments

Comments
 (0)