Skip to content

Break some unfortunate dependencies in jitc_yk #1545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 2 additions & 135 deletions ykrt/src/compile/jitc_yk/codegen/reg_alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@
//! - describes the generic interface to register allocators.
//! - contains concrete implementations of register allocators.

use crate::compile::jitc_yk::jit_ir::{InstIdx, Module};
use dynasmrt::x64::{Rq, Rx};

/// Where is an SSA variable stored?
///
/// FIXME: Too much of this is hard-coded to the x64 backend.
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum VarLocation {
pub(crate) enum VarLocation<R> {
/// The SSA variable is on the stack of the of the executed trace or the main interpreter loop.
/// Since we execute the trace on the main interpreter frame we can't distinguish the two.
///
Expand All @@ -34,7 +29,7 @@ pub(crate) enum VarLocation {
/// The SSA variable is in a register.
///
/// Note: two SSA variables can alias to the same `Register` location.
Register(Register),
Register(R),
/// A constant integer `bits` wide (see [jit_ir::Const::ConstInt] for the constraints on the
/// bit width) and with value `v`.
ConstInt { bits: u32, v: u64 },
Expand All @@ -43,131 +38,3 @@ pub(crate) enum VarLocation {
/// A constant pointer.
ConstPtr(usize),
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum Register {
GP(Rq), // general purpose
FP(Rx), // floating point
}

#[cfg(target_arch = "x86_64")]
impl VarLocation {
pub(crate) fn from_yksmp_location(m: &Module, iidx: InstIdx, x: &yksmp::Location) -> Self {
match x {
yksmp::Location::Register(0, ..) => VarLocation::Register(Register::GP(Rq::RAX)),
yksmp::Location::Register(1, ..) => {
// Since the control point passes the stackmap ID via RDX this case only happens in
// side-traces.
VarLocation::Register(Register::GP(Rq::RDX))
}
yksmp::Location::Register(2, ..) => VarLocation::Register(Register::GP(Rq::RCX)),
yksmp::Location::Register(3, ..) => VarLocation::Register(Register::GP(Rq::RBX)),
yksmp::Location::Register(4, ..) => VarLocation::Register(Register::GP(Rq::RSI)),
yksmp::Location::Register(5, ..) => VarLocation::Register(Register::GP(Rq::RDI)),
yksmp::Location::Register(8, ..) => VarLocation::Register(Register::GP(Rq::R8)),
yksmp::Location::Register(9, ..) => VarLocation::Register(Register::GP(Rq::R9)),
yksmp::Location::Register(10, ..) => VarLocation::Register(Register::GP(Rq::R10)),
yksmp::Location::Register(11, ..) => VarLocation::Register(Register::GP(Rq::R11)),
yksmp::Location::Register(12, ..) => VarLocation::Register(Register::GP(Rq::R12)),
yksmp::Location::Register(13, ..) => VarLocation::Register(Register::GP(Rq::R13)),
yksmp::Location::Register(14, ..) => VarLocation::Register(Register::GP(Rq::R14)),
yksmp::Location::Register(15, ..) => VarLocation::Register(Register::GP(Rq::R15)),
yksmp::Location::Register(x, ..) if *x >= 17 && *x <= 32 => VarLocation::Register(
Register::FP(super::x64::lsregalloc::FP_REGS[usize::from(x - 17)]),
),
yksmp::Location::Direct(6, off, size) => VarLocation::Direct {
frame_off: *off,
size: usize::from(*size),
},
// Since the trace shares the same stack frame as the main interpreter loop, we can
// translate indirect locations into normal stack locations. Note that while stackmaps
// use negative offsets, we use positive offsets for stack locations.
yksmp::Location::Indirect(6, off, size) => VarLocation::Stack {
frame_off: u32::try_from(*off * -1).unwrap(),
size: usize::from(*size),
},
yksmp::Location::Constant(v) => {
// FIXME: This isn't fine-grained enough, as there may be constants of any
// bit-size.
let byte_size = m.inst(iidx).def_byte_size(m);
debug_assert!(byte_size <= 8);
VarLocation::ConstInt {
bits: u32::try_from(byte_size).unwrap() * 8,
v: u64::from(*v),
}
}
e => {
todo!("{:?}", e);
}
}
}
}

impl From<&VarLocation> for yksmp::Location {
fn from(val: &VarLocation) -> Self {
match val {
VarLocation::Stack { frame_off, size } => {
// A stack location translates is an offset in relation to RBP which has the DWARF
// number 6.
yksmp::Location::Indirect(
6,
-i32::try_from(*frame_off).unwrap(),
u16::try_from(*size).unwrap(),
)
}
VarLocation::Direct { frame_off, size } => {
yksmp::Location::Direct(6, *frame_off, u16::try_from(*size).unwrap())
}
VarLocation::Register(reg) => {
let dwarf = match reg {
Register::GP(reg) => match reg {
Rq::RAX => 0,
Rq::RDX => 1,
Rq::RCX => 2,
Rq::RBX => 3,
Rq::RSI => 4,
Rq::RDI => 5,
Rq::R8 => 8,
Rq::R9 => 9,
Rq::R10 => 10,
Rq::R11 => 11,
Rq::R12 => 12,
Rq::R13 => 13,
Rq::R14 => 14,
Rq::R15 => 15,
e => todo!("{:?}", e),
},
Register::FP(reg) => match reg {
Rx::XMM0 => 17,
Rx::XMM1 => 18,
Rx::XMM2 => 19,
Rx::XMM3 => 20,
Rx::XMM4 => 21,
Rx::XMM5 => 22,
Rx::XMM6 => 23,
Rx::XMM7 => 24,
Rx::XMM8 => 25,
Rx::XMM9 => 26,
Rx::XMM10 => 27,
Rx::XMM11 => 28,
Rx::XMM12 => 29,
Rx::XMM13 => 30,
Rx::XMM14 => 31,
Rx::XMM15 => 32,
},
};
// We currently only use 8 byte registers, so the size is constant. Since these are
// JIT values there are no extra locations we need to worry about.
yksmp::Location::Register(dwarf, 8, Vec::new())
}
VarLocation::ConstInt { bits, v } => {
if *bits <= 32 {
yksmp::Location::Constant(u32::try_from(*v).unwrap())
} else {
todo!(">32 bit constant")
}
}
e => todo!("{:?}", e),
}
}
}
11 changes: 2 additions & 9 deletions ykrt/src/compile/jitc_yk/codegen/x64/deopt.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
use crate::{
aotsmp::AOT_STACKMAPS,
compile::{
jitc_yk::codegen::reg_alloc::{Register, VarLocation},
GuardIdx,
},
log::Verbosity,
mt::MTThread,
};
use super::{Register, VarLocation};
use crate::{aotsmp::AOT_STACKMAPS, compile::GuardIdx, log::Verbosity, mt::MTThread};
use dynasmrt::Register as _;
use libc::c_void;
#[cfg(debug_assertions)]
Expand Down
32 changes: 11 additions & 21 deletions ykrt/src/compile/jitc_yk/codegen/x64/lsregalloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,17 @@
//! where it has spilled an instruction's value: it guarantees to spill an instruction to at most
//! one place on the stack.

use super::rev_analyse::RevAnalyse;
use super::{rev_analyse::RevAnalyse, Register, VarLocation};
use crate::compile::jitc_yk::{
codegen::{
abs_stack::AbstractStack,
reg_alloc::{self, VarLocation},
x64::REG64_BYTESIZE,
},
codegen::{abs_stack::AbstractStack, x64::REG64_BYTESIZE},
jit_ir::{Const, ConstIdx, FloatTy, Inst, InstIdx, Module, Operand, PtrAddInst, Ty},
};
use dynasmrt::{
dynasm,
x64::{
Assembler, {Rq, Rx},
},
DynasmApi, Register,
DynasmApi, Register as dynasmrtRegister,
};
use std::{marker::PhantomData, mem};

Expand Down Expand Up @@ -445,8 +441,7 @@ impl LSRegAlloc<'_> {
// Deal with `OutputCanBeSameAsInput`.
for i in 0..constraints.len() {
if let RegConstraint::OutputCanBeSameAsInput(search_op) = constraints[i].clone() {
if let Some(reg_alloc::Register::GP(reg)) = self.rev_an.reg_hints[usize::from(iidx)]
{
if let Some(Register::GP(reg)) = self.rev_an.reg_hints[usize::from(iidx)] {
if avoid.is_set(reg) {
continue;
}
Expand Down Expand Up @@ -475,9 +470,7 @@ impl LSRegAlloc<'_> {
RegConstraint::Output
| RegConstraint::OutputCanBeSameAsInput(_)
| RegConstraint::InputOutput(_) => {
if let Some(reg_alloc::Register::GP(reg)) =
self.rev_an.reg_hints[usize::from(iidx)]
{
if let Some(Register::GP(reg)) = self.rev_an.reg_hints[usize::from(iidx)] {
if !avoid.is_set(reg) {
*cnstr = match cnstr {
RegConstraint::Output => RegConstraint::OutputFromReg(reg),
Expand Down Expand Up @@ -762,8 +755,7 @@ impl LSRegAlloc<'_> {
if self.is_inst_var_still_used_after(cur_iidx, query_iidx) {
let mut new_reg = None;
// Try to use `query_iidx`s hint, if there is one, and it's not in use...
if let Some(reg_alloc::Register::GP(reg)) =
self.rev_an.reg_hints[usize::from(query_iidx)]
if let Some(Register::GP(reg)) = self.rev_an.reg_hints[usize::from(query_iidx)]
{
if !self.gp_regset.is_set(reg) && !avoid.is_set(reg) {
new_reg = Some(reg);
Expand Down Expand Up @@ -913,15 +905,15 @@ impl LSRegAlloc<'_> {
false
}
}) {
VarLocation::Register(reg_alloc::Register::GP(GP_REGS[reg_i]))
VarLocation::Register(Register::GP(GP_REGS[reg_i]))
} else if let Some(reg_i) = self.fp_reg_states.iter().position(|x| {
if let RegState::FromInst(y) = x {
*y == iidx
} else {
false
}
}) {
VarLocation::Register(reg_alloc::Register::FP(FP_REGS[reg_i]))
VarLocation::Register(Register::FP(FP_REGS[reg_i]))
} else {
let inst = self.m.inst(iidx);
let size = inst.def_byte_size(self.m);
Expand Down Expand Up @@ -1037,9 +1029,7 @@ impl LSRegAlloc<'_> {
RegConstraint::Output
| RegConstraint::OutputCanBeSameAsInput(_)
| RegConstraint::InputOutput(_) => {
if let Some(reg_alloc::Register::FP(reg)) =
self.rev_an.reg_hints[usize::from(iidx)]
{
if let Some(Register::FP(reg)) = self.rev_an.reg_hints[usize::from(iidx)] {
if !avoid.is_set(reg) {
*cnstr = match cnstr {
RegConstraint::Output => RegConstraint::OutputFromReg(reg),
Expand Down Expand Up @@ -1446,7 +1436,7 @@ impl LSRegAlloc<'_> {
/// In the following `R` is a fixed register specified inside the variant, whereas *x* is an
/// unspecified register determined by the allocator.
#[derive(Clone, Debug)]
pub(crate) enum RegConstraint<R: Register> {
pub(crate) enum RegConstraint<R: dynasmrt::Register> {
/// Make sure `Operand` is loaded into a register *x* on entry; its value must be unchanged
/// after the instruction is executed.
Input(Operand),
Expand Down Expand Up @@ -1515,7 +1505,7 @@ enum RegState {
#[derive(Clone, Copy, Debug)]
struct RegSet<R>(u16, PhantomData<R>);

impl<R: Register> RegSet<R> {
impl<R: dynasmrt::Register> RegSet<R> {
/// Create a [RegSet] with all registers unused.
fn blank() -> Self {
Self(0, PhantomData)
Expand Down
Loading
Loading