Skip to content
Open
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
81 changes: 62 additions & 19 deletions src/codegen/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ fn compute_register_map(bbm: &BasicBlockManager) -> BTreeMap<RegisterIndex, Mach
out
}

// to detect liveness information inside a basic block we can:
// forall registers r where r is not liveout in this node,
// find the last read of r
//
// when building register map:
// iterate through IR instructions, if IR offset is the last use of a register,
// use that machine register
//
// to do this well, we'll need an iterator over IR points that contains
// variable declaration and use info. It can skip IR that don't use registers
// but it must return the proper index into the IR for every instruction that
// does use or declare registers.

fn build_register_map_inner(
bbm: &BasicBlockManager,
gq: &reg_alloc::GraphQuery,
Expand All @@ -78,7 +91,12 @@ fn build_register_map_inner(
// in cases where the parent has multiple paths]
let cm_copy = current_map.clone();
for (k, _) in cm_copy {
// having a bug with `is_live_in` here when running the example, TODO: fix
if !gq.is_live_in(k, cur_idx) {
println!(
"FREEING REGISTER {:?} because it's not used in this block",
k
);
let machine_reg = current_map.remove(&k).unwrap();
available_registers.push_back(machine_reg);
}
Expand All @@ -87,26 +105,49 @@ fn build_register_map_inner(
// TODO: generate liveness info from inside basic blocks too to reduce register pressure
// this should cause basic tests to fail in the short-term so should be implemented
// very soon
for declared_reg in bbm.get(cur_idx).unwrap().iter_defined_registers() {
let machine_reg = available_registers
.pop_front()
.expect("Ran out of machine registers! Need to implement register spilling");
let existing_reg = current_map.insert(*declared_reg, machine_reg);
assert!(existing_reg.is_none());
let existing_reg = reg_map.insert(*declared_reg, machine_reg);
assert!(existing_reg.is_none());
}
let rev_last_used_map = bbm.get(cur_idx).unwrap().get_reverse_last_used_map();

let mut to_clean_up = VecDeque::new();
dbg!("begin bb");
for (i, inst) in bbm.get(cur_idx).unwrap().iterate_instructions().enumerate() {
dbg!(&current_map);
if let Some(last_useds) = rev_last_used_map.get(&(i as u32)) {
for last_used in last_useds.iter() {
if !gq.is_live_out(*last_used, cur_idx) {
if let Some(machine_reg) = current_map.remove(&last_used) {
println!("FREEING {:?}", last_used);
available_registers.push_front(machine_reg);
} else {
// if a register is never used again but we haven't seen
// it yet, then we'll clean it up after we use it
dbg!(last_used);
to_clean_up.push_back(last_used);
}
} else {
dbg!("USED AGAIN!");
dbg!(last_used);
}
}
}
dbg!(inst);
if let Some(v) = inst.get_defined_register() {
dbg!(v);
let machine_reg = available_registers
.pop_front()
.expect("Ran out of machine registers! Need to implement register spilling");
let existing_reg = current_map.insert(*v, machine_reg);
assert!(existing_reg.is_none());
let existing_reg = reg_map.insert(*v, machine_reg);
assert!(existing_reg.is_none());
}

// =====================================================
// free registers that are not used on any path after
// TODO: optimize
let cm_copy = current_map.clone();
for (k, _) in cm_copy {
if !gq.is_live_out(k, cur_idx) {
let machine_reg = current_map.remove(&k).unwrap();
available_registers.push_back(machine_reg);
while let Some(reg) = to_clean_up.pop_front() {
println!("CLEANING UP DOWN HERE! {:?}", reg);
let mr = current_map.remove(&reg).unwrap();
available_registers.push_front(mr);
}
}

for exit in bbm.get(cur_idx).unwrap().iter_exits() {
build_register_map_inner(
bbm,
Expand Down Expand Up @@ -339,7 +380,8 @@ pub fn generate_code(ctx: &Context) -> Result<(ExecutableBuffer, AssemblyOffset)
Value::Immediate { _type, value: v1 },
Value::Immediate { value: v2, .. },
) => {
emit_mov_imm(&mut ops, mdest, v1 + v2, _type);
// TODO: decide overflow behavior
emit_mov_imm(&mut ops, mdest, v1.wrapping_add(v2), _type);
}
}
}
Expand Down Expand Up @@ -380,7 +422,8 @@ pub fn generate_code(ctx: &Context) -> Result<(ExecutableBuffer, AssemblyOffset)
Value::Immediate { _type, value: v1 },
Value::Immediate { value: v2, .. },
) => {
emit_mov_imm(&mut ops, mdest, v1 - v2, _type);
// TODO: decide underflow behavior
emit_mov_imm(&mut ops, mdest, v1.wrapping_sub(v2), _type);
}
}
}
Expand Down
43 changes: 34 additions & 9 deletions src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use smallvec::SmallVec;
use std::collections::BTreeMap;
use std::sync::{mpsc, Mutex};

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
Expand Down Expand Up @@ -133,6 +134,18 @@ impl IR {
}
out
}

pub fn get_defined_register(&self) -> Option<&RegisterIndex> {
match self {
IR::Alloca { dest_register, .. }
| IR::Add { dest_register, .. }
| IR::Subtract { dest_register, .. }
| IR::Multiply { dest_register, .. }
| IR::Load { dest_register, .. }
| IR::Divide { dest_register, .. } => Some(dest_register),
_ => None,
}
}
}

/// Top level type to generate IR with
Expand Down Expand Up @@ -236,20 +249,32 @@ impl BasicBlock {
self.exits.iter()
}
pub(crate) fn iter_defined_registers(&self) -> impl Iterator<Item = &RegisterIndex> {
self.code.iter().filter_map(|c| match c {
IR::Alloca { dest_register, .. }
| IR::Add { dest_register, .. }
| IR::Subtract { dest_register, .. }
| IR::Multiply { dest_register, .. }
| IR::Load { dest_register, .. }
| IR::Divide { dest_register, .. } => Some(dest_register),
_ => None,
})
self.code.iter().filter_map(IR::get_defined_register)
}
pub(crate) fn iter_used_registers(&self) -> impl Iterator<Item = &RegisterIndex> {
self.code.iter().flat_map(|c| c.get_used_registers())
}

pub(crate) fn get_last_used_map(&self) -> BTreeMap<RegisterIndex, u32> {
let mut out = BTreeMap::new();
for (i, inst) in self.code.iter().enumerate() {
for used_register in inst.get_used_registers() {
let ent = out.entry(*used_register).or_default();
*ent = i as u32;
}
}
out
}

pub(crate) fn get_reverse_last_used_map(&self) -> BTreeMap<u32, SmallVec<[RegisterIndex; 2]>> {
let mut out: BTreeMap<u32, SmallVec<[RegisterIndex; 2]>> = BTreeMap::new();
for (k, v) in self.get_last_used_map() {
let ent = out.entry(v).or_default();
ent.push(k);
}
out
}

pub fn finish(&mut self) {}

pub(crate) fn iterate_instructions(&self) -> impl Iterator<Item = &IR> {
Expand Down