This commit is contained in:
2026-07-18 13:33:59 -04:00
parent 6cc81d7a5c
commit c062993130
14 changed files with 429 additions and 68 deletions
+188 -8
View File
@@ -1,14 +1,28 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet, VecDeque};
use super::*;
use crate::backend::{LibImport, LinkedProgram, SymImport, SymTable, Symbol};
use util::*;
use crate::backend::{Body, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId};
pub struct Encoder<'a> {
pub code: Code,
pub sym_tab: SymTable<u64>,
pub sym_refs: HashMap<Symbol, Vec<usize>>,
pub program: &'a Program<X86_64>,
pub vars: VarMap,
}
#[derive(Clone, Copy)]
pub struct VarUse {
pos: usize,
reg: Option<Reg>,
}
pub type VarUses = HashMap<VarId, VecDeque<VarUse>>;
pub type RegUses = [VecDeque<usize>; 16];
impl VarUse {
pub fn min(self, other: Self) -> Self {
if self.pos <= other.pos { self } else { other }
}
}
pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
@@ -19,7 +33,14 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
for f in &p.funcs {
let addr = encoder.code.bytes.len();
encoder.sym_tab.insert(f.sym, addr as u64);
for instr in &f.instrs {
let mut segments = Vec::new();
calc_uses(&mut segments, &f.body);
}
for f in &p.funcs {
let addr = encoder.code.bytes.len();
encoder.sym_tab.insert(f.sym, addr as u64);
for instr in &f.body {
encoder.compile_instr(instr)?;
}
}
@@ -60,24 +81,183 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
})
}
#[derive(Default)]
pub struct SegUses {
var: VarUses,
reg: RegUses,
}
pub fn calc_uses(segments: &mut Vec<SegUses>, body: &Body<X86_64>) -> (usize, usize) {
let mut pos = 0;
let seg_i = segments.len();
segments.push(Default::default());
let mut vars = VarUses::default();
let mut regs = RegUses::default();
let volatile = [
rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15,
]
.map(|r| r.reg());
macro_rules! push {
($var:ident) => {
push!($var, VarUse { pos: 0, reg: None })
};
($var:ident, $pos:expr) => {
vars.entry($var.clone()).or_default().push_back(VarUse {
pos: $pos.pos + pos,
reg: $pos.reg,
})
};
}
for instr in body {
match instr {
BInstr::Set { dst, src: _ } => push!(dst),
BInstr::Call { dst, f, args } => {
push!(dst);
for (i, arg) in args.iter().enumerate() {
push!(arg);
if i < 8 {
regs[i].push_back(pos);
}
}
}
BInstr::Copy { dst, src } => {
push!(dst);
push!(src);
}
BInstr::Add { dst, src1, src2 } => {
push!(dst);
push!(src1);
push!(src2);
}
BInstr::If { cond, then, else_ } => {
push!(cond);
pos += 1;
let (seg_i1, len1) = calc_uses(segments, then);
let (seg_i2, len2) = calc_uses(segments, else_);
// insert closest usages
for (var, poss1) in &segments[seg_i1].var {
if let Some(poss2) = segments[seg_i2].var.get(var) {
push!(var, poss1[0].min(poss2[0]));
} else {
push!(var, poss1[0]);
}
}
for (var, poss2) in &segments[seg_i2].var {
if !segments[seg_i1].var.contains_key(var) {
push!(var, poss2[0]);
}
}
pos += len1.max(len2);
continue;
}
BInstr::Loop(instrs) => {
let (seg_i2, len) = calc_uses(segments, instrs);
// insert closest usages
for (var, poss) in &segments[seg_i2].var {
push!(var, poss[0]);
}
// during register allocation, want to insert the first use of each var
// at len + use, because if it jumps back up, that will be the next
// usage rather than if the loop exits; pad ensures there is room so
// the insertions are always before the rest of the usages (after loop)
let pad = (len * 2).saturating_sub(pos);
pos += len + pad;
continue;
}
BInstr::Break(var) => {
if let Some(var) = var {
push!(var);
}
}
BInstr::Return(var) => {
if let Some(var) = var {
push!(var);
}
}
BInstr::Asm(asm) => {
for &(reg, var) in &asm.args {
push!(
var,
VarUse {
pos: 0,
reg: Some(reg)
}
);
regs[reg as usize].push_back(pos);
}
}
}
pos += 1;
}
segments[seg_i].var = vars;
segments[seg_i].reg = regs;
return (seg_i, pos);
}
type BInstr = crate::backend::Instr<X86_64>;
impl<'a> Encoder<'a> {
fn compile_instr(&mut self, instr: &BInstr) -> Result<(), CompilerMsg> {
fn compile_instr(&mut self, instr: &BInstr) -> EncodeRes {
match instr {
BInstr::Asm(asm) => {
self.code.extend(asm);
}
BInstr::Asm(asm) => self.asm(asm)?,
_ => todo!(),
}
Ok(())
}
pub fn asm(&mut self, asm: &Asm) -> EncodeRes {
let mut used = HashSet::default();
let mut vars = HashSet::default();
for &instr in &asm.instrs {
if let Some(regw) = instr.regw_set() {
used.insert(regw.reg());
}
for var in instr.vars() {
vars.insert(var);
}
}
let overlap = used.intersection(&self.vars.active.values());
for var in &vars {
if let Some(reg) = self.vars.active.get(var)
&& used.contains(reg)
{}
}
for &instr in &asm.instrs {
match instr {
Instr::Mov { dst, src } => self.code.mov(dst, src)?,
Instr::Push(rvmi) => todo!(),
Instr::Pop(rvm) => todo!(),
Instr::Lea { dst, src } => todo!(),
Instr::Int(code) => self.code.int(code),
Instr::Syscall => self.code.syscall(),
Instr::Call(sym) => self.code.call(sym),
Instr::CallAt(sym) => self.code.call_mem(sym),
Instr::Ret => self.code.ret(),
Instr::Add { dst, src } => todo!(),
Instr::Sub { dst, src } => todo!(),
}
}
Ok(())
}
pub fn new(program: &'a Program<X86_64>) -> Self {
Self {
code: Code::default(),
sym_tab: SymTable::new(program.sym_count()),
sym_refs: Default::default(),
vars: VarMap::default(),
program,
}
}
}
pub struct VarMap {
active: HashMap<VarId, Reg>,
}
impl Default for VarMap {
fn default() -> Self {
Self {
active: Default::default(),
}
}
}