mir register allcoator
This commit is contained in:
parent
c13ed77ddf
commit
16399192b3
|
@ -8,5 +8,6 @@ ansi_term = "0.12.1"
|
||||||
clap = "4.5.14"
|
clap = "4.5.14"
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
|
petgraph = "0.6.5"
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
unicode-xid = "0.2.4"
|
unicode-xid = "0.2.4"
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
iter_advance_by,
|
iter_advance_by,
|
||||||
box_into_inner,
|
box_into_inner,
|
||||||
hash_extract_if,
|
hash_extract_if,
|
||||||
bigint_helper_methods
|
bigint_helper_methods,
|
||||||
|
map_try_insert
|
||||||
)]
|
)]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
|
216
src/mir.rs
216
src/mir.rs
|
@ -1,5 +1,10 @@
|
||||||
//! Machine-level Intermediate Representation
|
//! Machine-level Intermediate Representation
|
||||||
|
|
||||||
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use petgraph::visit::{IntoEdges, NodeRef};
|
||||||
|
|
||||||
use crate::string_table::{Index as StringsIndex, StringTable};
|
use crate::string_table::{Index as StringsIndex, StringTable};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
@ -150,6 +155,43 @@ pub enum Inst {
|
||||||
//FunctionStart,
|
//FunctionStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Inst {
|
||||||
|
fn has_value(&self) -> bool {
|
||||||
|
// basically, when an arithmetic instruction has two immediates, then just replace it with a mov into the dst reg
|
||||||
|
match self {
|
||||||
|
Inst::Label
|
||||||
|
| Inst::ConstantBytes
|
||||||
|
| Inst::ConstantByte
|
||||||
|
| Inst::ConstantWord
|
||||||
|
| Inst::ConstantDWord
|
||||||
|
| Inst::ConstantQWord
|
||||||
|
| Inst::ExternRef
|
||||||
|
| Inst::Alloca
|
||||||
|
| Inst::Store(_)
|
||||||
|
| Inst::ReturnValue
|
||||||
|
| Inst::Return => false,
|
||||||
|
Inst::GetElementPtr(_)
|
||||||
|
| Inst::Load(_)
|
||||||
|
| Inst::Parameter(_)
|
||||||
|
| Inst::Add(_)
|
||||||
|
| Inst::Sub(_)
|
||||||
|
| Inst::Mul(_)
|
||||||
|
| Inst::MulSigned(_)
|
||||||
|
| Inst::Div(_)
|
||||||
|
| Inst::DivSigned(_)
|
||||||
|
| Inst::Rem(_)
|
||||||
|
| Inst::RemSigned(_)
|
||||||
|
| Inst::BitAnd(_)
|
||||||
|
| Inst::BitOr(_)
|
||||||
|
| Inst::BitXOr(_)
|
||||||
|
| Inst::Negate(_)
|
||||||
|
| Inst::ShiftLeft(_)
|
||||||
|
| Inst::ShiftRightSigned(_)
|
||||||
|
| Inst::ShiftRightUnsigned(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub union Data {
|
pub union Data {
|
||||||
none: (),
|
none: (),
|
||||||
|
@ -397,11 +439,17 @@ impl Mir {
|
||||||
&self,
|
&self,
|
||||||
w: &mut W,
|
w: &mut W,
|
||||||
strings: &StringTable,
|
strings: &StringTable,
|
||||||
|
reg_alloc: &HashMap<u32, amd64::Register>,
|
||||||
node: u32,
|
node: u32,
|
||||||
) -> core::fmt::Result {
|
) -> core::fmt::Result {
|
||||||
let idx = node as usize;
|
let idx = node as usize;
|
||||||
let inst = self.nodes[idx];
|
let inst = self.nodes[idx];
|
||||||
let data = self.data[idx];
|
let data = self.data[idx];
|
||||||
|
|
||||||
|
if let Some(reg) = reg_alloc.get(&node) {
|
||||||
|
write!(w, "({reg}) ")?;
|
||||||
|
}
|
||||||
|
|
||||||
match inst {
|
match inst {
|
||||||
Inst::Label => writeln!(w, "%{node} = {}", strings.get_str(data.as_index())),
|
Inst::Label => writeln!(w, "%{node} = {}", strings.get_str(data.as_index())),
|
||||||
Inst::ConstantBytes => writeln!(
|
Inst::ConstantBytes => writeln!(
|
||||||
|
@ -507,8 +555,9 @@ impl Mir {
|
||||||
w: &mut W,
|
w: &mut W,
|
||||||
strings: &StringTable,
|
strings: &StringTable,
|
||||||
) -> core::fmt::Result {
|
) -> core::fmt::Result {
|
||||||
|
let reg_alloc = self.build_liveness();
|
||||||
for node in 0..self.nodes.len() {
|
for node in 0..self.nodes.len() {
|
||||||
self.render_node(w, strings, node as u32)?;
|
self.render_node(w, strings, ®_alloc, node as u32)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -518,6 +567,171 @@ impl Mir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mir {
|
||||||
|
pub fn build_liveness(&self) -> HashMap<u32, amd64::Register> {
|
||||||
|
struct Interval {
|
||||||
|
start: u32,
|
||||||
|
end: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut references = BTreeMap::<u32, Vec<u32>>::new();
|
||||||
|
let mut prefered_colors = BTreeMap::<u32, amd64::Register>::new();
|
||||||
|
use amd64::Register::*;
|
||||||
|
let mut param_registers = [rsi, rdi, rdx, rcx, r8, r9]
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for i in 0..self.nodes.len() {
|
||||||
|
let inst = self.nodes[i];
|
||||||
|
let data = self.data[i];
|
||||||
|
|
||||||
|
let node = i as u32;
|
||||||
|
references.insert(node, Vec::new());
|
||||||
|
|
||||||
|
match inst {
|
||||||
|
Inst::Parameter(_) => {
|
||||||
|
if let Some(reg) = param_registers.pop() {
|
||||||
|
println!("prefering {reg} for param");
|
||||||
|
prefered_colors.insert(node, reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Negate(_) | Inst::Load(_) => {
|
||||||
|
references.get_mut(&data.as_node()).unwrap().push(node);
|
||||||
|
}
|
||||||
|
// return is thru rax.
|
||||||
|
Inst::ReturnValue => {
|
||||||
|
let val = data.as_node();
|
||||||
|
references.get_mut(&val).unwrap().push(node);
|
||||||
|
_ = prefered_colors.try_insert(val, amd64::Register::rax);
|
||||||
|
}
|
||||||
|
Inst::GetElementPtr(_) => {
|
||||||
|
let (src, _) = data.as_binary();
|
||||||
|
references.get_mut(&src).unwrap().push(node);
|
||||||
|
}
|
||||||
|
// mul wants lhs (or rhs) to be rax, imul doesn't care as much.
|
||||||
|
// note that it also clobers rdx
|
||||||
|
Inst::Mul(ty) | Inst::MulSigned(ty) => {
|
||||||
|
let (lhs, rhs) = data.as_binary();
|
||||||
|
references.get_mut(&lhs).unwrap().push(node);
|
||||||
|
references.get_mut(&rhs).unwrap().push(node);
|
||||||
|
|
||||||
|
if !ty.is_floating() {
|
||||||
|
_ = prefered_colors.try_insert(lhs, amd64::Register::rax);
|
||||||
|
_ = prefered_colors.try_insert(rhs, amd64::Register::rax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mul wants lhs to be rax, imul can do either.
|
||||||
|
// note that it also clobers rdx
|
||||||
|
Inst::Div(_) | Inst::DivSigned(_) | Inst::Rem(_) | Inst::RemSigned(_) => {
|
||||||
|
let (lhs, rhs) = data.as_binary();
|
||||||
|
references.get_mut(&lhs).unwrap().push(node);
|
||||||
|
references.get_mut(&rhs).unwrap().push(node);
|
||||||
|
_ = prefered_colors.try_insert(lhs, amd64::Register::rax);
|
||||||
|
}
|
||||||
|
// shr,shl,sar want the shift to be in cl.
|
||||||
|
Inst::ShiftLeft(_) | Inst::ShiftRightSigned(_) | Inst::ShiftRightUnsigned(_) => {
|
||||||
|
let (lhs, rhs) = data.as_binary();
|
||||||
|
references.get_mut(&lhs).unwrap().push(node);
|
||||||
|
references.get_mut(&rhs).unwrap().push(node);
|
||||||
|
_ = prefered_colors.try_insert(rhs, amd64::Register::rcx);
|
||||||
|
}
|
||||||
|
// add,adc,sub,sbb,or,and,xor and mov don't care much about their source registers
|
||||||
|
Inst::Add(_)
|
||||||
|
| Inst::Sub(_)
|
||||||
|
| Inst::BitAnd(_)
|
||||||
|
| Inst::BitOr(_)
|
||||||
|
| Inst::BitXOr(_)
|
||||||
|
| Inst::Store(_) => {
|
||||||
|
let (lhs, rhs) = data.as_binary();
|
||||||
|
references.get_mut(&lhs).unwrap().push(node);
|
||||||
|
references.get_mut(&rhs).unwrap().push(node);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
references.retain(|&node, refs| !refs.is_empty() && self.nodes[node as usize].has_value());
|
||||||
|
prefered_colors.retain(|&node, _| self.nodes[node as usize].has_value());
|
||||||
|
|
||||||
|
let intervals = references
|
||||||
|
.iter()
|
||||||
|
.map(|(node, refs)| {
|
||||||
|
(
|
||||||
|
*node,
|
||||||
|
Interval {
|
||||||
|
start: *node,
|
||||||
|
end: refs.last().cloned().unwrap(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<BTreeMap<u32, Interval>>();
|
||||||
|
|
||||||
|
let mut edges = HashSet::<(u32, u32)>::new();
|
||||||
|
for (&node, interval) in &intervals {
|
||||||
|
for (&other, _) in references.range(interval.start + 1..interval.end) {
|
||||||
|
edges.insert((node, other));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let inference_graph = petgraph::graph::UnGraph::<(), ()>::from_edges(edges.into_iter());
|
||||||
|
|
||||||
|
let gprs = amd64::Register::gp_registers();
|
||||||
|
let sses = amd64::Register::sse_registers();
|
||||||
|
|
||||||
|
let mut assigned_colors = HashMap::<u32, amd64::Register>::new();
|
||||||
|
for &node in references.keys().rev() {
|
||||||
|
if matches!(self.nodes[node as usize], Inst::Alloca) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clique_colors = inference_graph
|
||||||
|
.neighbors(node.into())
|
||||||
|
.filter_map(|e| assigned_colors.get(&(e.index() as u32)).cloned())
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
let clique_preferred_colors = inference_graph
|
||||||
|
.neighbors(node.into())
|
||||||
|
.filter_map(|e| prefered_colors.get(&(e.index() as u32)).cloned())
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
|
let color = prefered_colors
|
||||||
|
.get(&node)
|
||||||
|
.into_iter()
|
||||||
|
.chain(gprs.iter())
|
||||||
|
.filter(|reg| !clique_colors.contains(reg))
|
||||||
|
.find_or_first(|®| !clique_preferred_colors.contains(reg))
|
||||||
|
.cloned()
|
||||||
|
.expect("ran out of registers");
|
||||||
|
|
||||||
|
println!("%{node} wants {:?}\n\tclique: {clique_colors:?}\n\tclique prefs: {clique_preferred_colors:?}\n\t-> {color}", prefered_colors.get(&node));
|
||||||
|
|
||||||
|
prefered_colors.remove(&node);
|
||||||
|
|
||||||
|
assigned_colors.insert(node, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eprintln!(
|
||||||
|
// "Inference Graph:\n{:?}",
|
||||||
|
// petgraph::dot::Dot::with_attr_getters(
|
||||||
|
// &inference_graph,
|
||||||
|
// &[
|
||||||
|
// petgraph::dot::Config::EdgeNoLabel,
|
||||||
|
// petgraph::dot::Config::NodeNoLabel,
|
||||||
|
// ],
|
||||||
|
// &Box::new(|_, _| { "".to_owned() }),
|
||||||
|
// &Box::new(|_, node: (petgraph::graph::NodeIndex, &())| {
|
||||||
|
// let (node, _) = node;
|
||||||
|
// if let Some(reg) = assigned_colors.get(&(node.index() as u32)) {
|
||||||
|
// format!("label=\"{} ({})\"", node.index(), reg)
|
||||||
|
// } else {
|
||||||
|
// format!("label=\"{}\"", node.index())
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
|
||||||
|
assigned_colors
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct DisplayMir<'a, 'b> {
|
pub struct DisplayMir<'a, 'b> {
|
||||||
mir: &'a Mir,
|
mir: &'a Mir,
|
||||||
strings: &'b StringTable,
|
strings: &'b StringTable,
|
||||||
|
|
|
@ -17,6 +17,9 @@ impl Index {
|
||||||
pub fn new(start: u32, end: u32) -> Self {
|
pub fn new(start: u32, end: u32) -> Self {
|
||||||
Self { start, end }
|
Self { start, end }
|
||||||
}
|
}
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self::new(u32::MAX, u32::MAX)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
Loading…
Reference in a new issue