blob: a74a60ec26ab437a17e01546711173e55ef24bff [file] [log] [blame] [edit]
use core::fmt;
use alloc::{format, string::ToString, vec::Vec};
use serde::{Deserialize, Serialize};
use crate::{Block, Function, Inst, InstRange, MachineEnv, Operand, PRegSet, RegClass, VReg};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
enum InstOpcode {
Op,
Ret,
Branch,
}
impl fmt::Display for InstOpcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InstOpcode::Op => f.write_str("op"),
InstOpcode::Ret => f.write_str("ret"),
InstOpcode::Branch => f.write_str("branch"),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct InstData {
op: InstOpcode,
operands: Vec<Operand>,
clobbers: PRegSet,
is_safepoint: bool,
}
/// A wrapper around a `Function` and `MachineEnv` that can be serialized and
/// deserialized.
///
/// The serialized form of this structure is not stable: it is intended to be
/// deserialized with the exact same version of regalloc2 as the one that it
/// was created with.
#[derive(Serialize, Deserialize)]
pub struct SerializableFunction {
machine_env: MachineEnv,
entry_block: Block,
insts: Vec<InstData>,
blocks: Vec<InstRange>,
block_preds: Vec<Vec<Block>>,
block_succs: Vec<Vec<Block>>,
block_params_in: Vec<Vec<VReg>>,
block_params_out: Vec<Vec<Vec<VReg>>>,
num_vregs: usize,
reftype_vregs: Vec<VReg>,
debug_value_labels: Vec<(VReg, Inst, Inst, u32)>,
spillslot_size: Vec<usize>,
multi_spillslot_named_by_last_slot: bool,
allow_multiple_vreg_defs: bool,
}
impl SerializableFunction {
/// Creates a new `SerializableFunction` from an arbitray `Function` and
/// `MachineEnv`.
pub fn new(func: &impl Function, machine_env: MachineEnv) -> Self {
Self {
machine_env,
entry_block: func.entry_block(),
insts: (0..func.num_insts())
.map(|i| {
let inst = Inst::new(i);
let op = if func.is_ret(inst) {
InstOpcode::Ret
} else if func.is_branch(inst) {
InstOpcode::Branch
} else {
InstOpcode::Op
};
InstData {
op,
operands: func.inst_operands(inst).to_vec(),
clobbers: func.inst_clobbers(inst),
is_safepoint: func.requires_refs_on_stack(inst),
}
})
.collect(),
blocks: (0..func.num_blocks())
.map(|i| {
let block = Block::new(i);
func.block_insns(block)
})
.collect(),
block_preds: (0..func.num_blocks())
.map(|i| {
let block = Block::new(i);
func.block_preds(block).to_vec()
})
.collect(),
block_succs: (0..func.num_blocks())
.map(|i| {
let block = Block::new(i);
func.block_succs(block).to_vec()
})
.collect(),
block_params_in: (0..func.num_blocks())
.map(|i| {
let block = Block::new(i);
func.block_params(block).to_vec()
})
.collect(),
block_params_out: (0..func.num_blocks())
.map(|i| {
let block = Block::new(i);
let inst = func.block_insns(block).last();
(0..func.block_succs(block).len())
.map(|succ_idx| func.branch_blockparams(block, inst, succ_idx).to_vec())
.collect()
})
.collect(),
num_vregs: func.num_vregs(),
reftype_vregs: func.reftype_vregs().to_vec(),
debug_value_labels: func.debug_value_labels().to_vec(),
spillslot_size: [
func.spillslot_size(RegClass::Int),
func.spillslot_size(RegClass::Float),
func.spillslot_size(RegClass::Vector),
]
.to_vec(),
multi_spillslot_named_by_last_slot: func.multi_spillslot_named_by_last_slot(),
allow_multiple_vreg_defs: func.allow_multiple_vreg_defs(),
}
}
/// Returns the `MachineEnv` associated with this function.
pub fn machine_env(&self) -> &MachineEnv {
&self.machine_env
}
}
impl Function for SerializableFunction {
fn num_insts(&self) -> usize {
self.insts.len()
}
fn num_blocks(&self) -> usize {
self.blocks.len()
}
fn entry_block(&self) -> Block {
self.entry_block
}
fn block_insns(&self, block: Block) -> InstRange {
self.blocks[block.index()]
}
fn block_succs(&self, block: Block) -> &[Block] {
&self.block_succs[block.index()][..]
}
fn block_preds(&self, block: Block) -> &[Block] {
&self.block_preds[block.index()][..]
}
fn block_params(&self, block: Block) -> &[VReg] {
&self.block_params_in[block.index()][..]
}
fn is_ret(&self, insn: Inst) -> bool {
self.insts[insn.index()].op == InstOpcode::Ret
}
fn is_branch(&self, insn: Inst) -> bool {
self.insts[insn.index()].op == InstOpcode::Branch
}
fn branch_blockparams(&self, block: Block, _: Inst, succ: usize) -> &[VReg] {
&self.block_params_out[block.index()][succ][..]
}
fn requires_refs_on_stack(&self, insn: Inst) -> bool {
self.insts[insn.index()].is_safepoint
}
fn inst_operands(&self, insn: Inst) -> &[Operand] {
&self.insts[insn.index()].operands[..]
}
fn inst_clobbers(&self, insn: Inst) -> PRegSet {
self.insts[insn.index()].clobbers
}
fn num_vregs(&self) -> usize {
self.num_vregs
}
fn reftype_vregs(&self) -> &[VReg] {
&self.reftype_vregs[..]
}
fn debug_value_labels(&self) -> &[(VReg, Inst, Inst, u32)] {
&self.debug_value_labels[..]
}
fn spillslot_size(&self, regclass: RegClass) -> usize {
self.spillslot_size[regclass as usize]
}
fn multi_spillslot_named_by_last_slot(&self) -> bool {
self.multi_spillslot_named_by_last_slot
}
fn allow_multiple_vreg_defs(&self) -> bool {
self.allow_multiple_vreg_defs
}
}
impl fmt::Debug for SerializableFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{\n")?;
write!(f, " machine_env: {:#?}\n", self.machine_env())?;
write!(
f,
" spillslot_size(Int): {}\n",
self.spillslot_size(RegClass::Int)
)?;
write!(
f,
" spillslot_size(Float): {}\n",
self.spillslot_size(RegClass::Float)
)?;
write!(
f,
" spillslot_size(Vector): {}\n",
self.spillslot_size(RegClass::Vector)
)?;
write!(
f,
" multi_spillslot_named_by_last_slot: {}\n",
self.multi_spillslot_named_by_last_slot()
)?;
write!(
f,
" allow_multiple_vreg_defs: {}\n",
self.allow_multiple_vreg_defs()
)?;
for vreg in self.reftype_vregs() {
write!(f, " REF: {}\n", vreg)?;
}
for (i, blockrange) in self.blocks.iter().enumerate() {
let succs = self.block_succs[i]
.iter()
.map(|b| b.index())
.collect::<Vec<_>>();
let preds = self.block_preds[i]
.iter()
.map(|b| b.index())
.collect::<Vec<_>>();
let params_in = self.block_params_in[i]
.iter()
.map(|v| format!("v{}", v.vreg()))
.collect::<Vec<_>>()
.join(", ");
let params_out = self.block_params_out[i]
.iter()
.enumerate()
.map(|(succ_idx, vec)| {
let succ = self.block_succs[i][succ_idx];
let params = vec
.iter()
.map(|v| format!("v{}", v.vreg()))
.collect::<Vec<_>>()
.join(", ");
format!("block{}({})", succ.index(), params)
})
.collect::<Vec<_>>()
.join(", ");
write!(
f,
" block{i}({params_in}): # succs:{succs:?} preds:{preds:?}\n",
)?;
for inst in blockrange.iter() {
if self.requires_refs_on_stack(inst) {
write!(f, " -- SAFEPOINT --\n")?;
}
let ops: Vec<_> = self
.inst_operands(inst)
.iter()
.map(|op| op.to_string())
.collect();
let ops = ops.join(", ");
let clobbers = if self.inst_clobbers(inst) == PRegSet::empty() {
format!("")
} else {
let clobbers: Vec<_> = self
.inst_clobbers(inst)
.into_iter()
.map(|preg| format!("Clobber: {preg}"))
.collect();
format!(", {}", clobbers.join(", "))
};
write!(
f,
" inst{}: {} {ops}{clobbers}\n",
inst.index(),
self.insts[inst.index()].op,
)?;
if let InstOpcode::Branch = self.insts[inst.index()].op {
write!(f, " params: {}\n", params_out)?;
}
}
}
write!(f, "}}\n")?;
Ok(())
}
}