This commit is contained in:
2026-07-19 20:43:49 -04:00
parent 1944683dc5
commit 9f7d95a67e
3 changed files with 156 additions and 166 deletions
+138 -150
View File
@@ -1,14 +1,15 @@
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use super::*; use super::*;
use crate::backend::{Body, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId}; use crate::backend::{Body, Func, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId};
pub struct Encoder<'a> { pub struct Encoder<'a> {
pub code: Code, pub code: Code,
pub sym_tab: SymTable<u64>, pub sym_tab: SymTable<u64>,
pub sym_refs: HashMap<Symbol, Vec<usize>>, pub sym_refs: HashMap<Symbol, Vec<usize>>,
pub program: &'a Program<X86_64>, pub program: &'a Program<X86_64>,
pub vars: VarMap, pub segs: Vec<SegUses>,
pub vars: HashMap<VarId, Reg>,
} }
pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> { pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
@@ -17,18 +18,7 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
p.encode_data(&mut encoder.code.bytes, &mut encoder.sym_tab); p.encode_data(&mut encoder.code.bytes, &mut encoder.sym_tab);
for f in &p.funcs { for f in &p.funcs {
let addr = encoder.code.bytes.len(); encoder.func(f);
encoder.sym_tab.insert(f.sym, addr as u64);
let mut segments = Vec::new();
calc_uses(&mut segments, &f.body, p);
}
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)?;
}
} }
for (pos, sym) in encoder.code.missing.drain(..) { for (pos, sym) in encoder.code.missing.drain(..) {
@@ -80,133 +70,143 @@ pub struct RegUse {
var: Option<VarId>, var: Option<VarId>,
} }
pub fn calc_uses(
segments: &mut Vec<SegUses>,
body: &Body<X86_64>,
p: &Program<X86_64>,
) -> (usize, usize) {
let mut pos = 0;
let seg_i = segments.len();
segments.push(Default::default());
let mut uses = SegUses::default();
macro_rules! push {
($var:ident) => {
push!($var, 0)
};
($var:ident, $pos:expr) => {
uses.var
.entry($var.clone())
.or_default()
.push_back(pos + $pos)
};
}
for instr in body {
match instr {
BInstr::Set { dst, src: _ } => push!(dst),
BInstr::Call { dst, f, args } => {
let conv = &p.call_convs[p.funcs[f].conv];
push!(dst);
for &reg in conv.scratch() {
uses.reg[reg as usize].push_back(RegUse { pos, var: None });
}
for (i, &arg) in args.iter().enumerate() {
push!(arg);
if let Some(&reg) = conv.param().get(i) {
uses.reg[reg as usize].back_mut().unwrap().var = Some(arg);
}
}
}
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, p);
let (seg_i2, len2) = calc_uses(segments, else_, p);
// 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, p);
// 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) => {
let mut used = HashSet::new();
for i in &asm.instrs {
if let Some(reg) = i.dst_reg() {
used.insert(reg.reg());
}
}
for &(reg, var) in &asm.args {
push!(var);
used.remove(&reg);
uses.reg[reg as usize].push_back(RegUse {
pos,
var: Some(var),
});
}
for reg in used {
uses.reg[reg as usize].push_back(RegUse { pos, var: None });
}
}
}
pos += 1;
}
segments[seg_i] = uses;
return (seg_i, pos);
}
type BInstr = crate::backend::Instr<X86_64>; type BInstr = crate::backend::Instr<X86_64>;
impl<'a> Encoder<'a> { impl<'a> Encoder<'a> {
fn compile_instr(&mut self, instr: &BInstr) -> EncodeRes { fn func(&mut self, f: &Func<X86_64>) -> EncodeRes {
match instr { let addr = self.code.bytes.len();
BInstr::Asm(asm) => self.asm(asm)?, self.sym_tab.insert(f.sym, addr as u64);
_ => todo!(), self.segs.clear();
self.calc_uses(&f.body);
for instr in &f.body {
match instr {
BInstr::Asm(asm) => self.asm(asm)?,
_ => todo!(),
}
} }
Ok(()) Ok(())
} }
pub fn calc_uses(&mut self, body: &Body<X86_64>) -> (usize, usize) {
let mut pos = 0;
let seg_i = self.segs.len();
self.segs.push(Default::default());
let mut uses = SegUses::default();
macro_rules! push {
($var:ident) => {
push!($var, 0)
};
($var:ident, $pos:expr) => {
uses.var
.entry($var.clone())
.or_default()
.push_back(pos + $pos)
};
}
for instr in body {
match instr {
BInstr::Set { dst, src: _ } => push!(dst),
BInstr::Call { dst, f, args } => {
let conv = &self.program.call_convs[self.program.funcs[f].conv];
push!(dst);
for &reg in conv.scratch() {
uses.reg[reg as usize].push_back(RegUse { pos, var: None });
}
for (i, &arg) in args.iter().enumerate() {
push!(arg);
if let Some(&reg) = conv.param().get(i) {
uses.reg[reg as usize].back_mut().unwrap().var = Some(arg);
}
}
}
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) = self.calc_uses(then);
let (seg_i2, len2) = self.calc_uses(else_);
// insert closest usages
for (var, poss1) in &self.segs[seg_i1].var {
if let Some(poss2) = self.segs[seg_i2].var.get(var) {
push!(var, poss1[0].min(poss2[0]));
} else {
push!(var, poss1[0]);
}
}
for (var, poss2) in &self.segs[seg_i2].var {
if !self.segs[seg_i1].var.contains_key(var) {
push!(var, poss2[0]);
}
}
pos += len1.max(len2);
continue;
}
BInstr::Loop(instrs) => {
let (seg_i2, len) = self.calc_uses(instrs);
// insert closest usages
for (var, poss) in &self.segs[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) => {
let mut used = HashSet::new();
for i in &asm.instrs {
if let Some(reg) = i.dst_reg() {
used.insert(reg.reg());
}
}
for &(reg, var) in &asm.args {
push!(var);
used.remove(&reg);
uses.reg[reg as usize].push_back(RegUse {
pos,
var: Some(var),
});
}
for reg in used {
uses.reg[reg as usize].push_back(RegUse { pos, var: None });
}
}
}
pos += 1;
}
self.segs[seg_i] = uses;
return (seg_i, pos);
}
pub fn rvm(&mut self, input: Rvm) -> RegMemKind {
match input {
Rvm::Reg(reg) => reg,
Rvm::Var(var_id) => todo!(),
Rvm::Mem(mem) => todo!(),
}
}
pub fn asm(&mut self, asm: &Asm) -> EncodeRes { pub fn asm(&mut self, asm: &Asm) -> EncodeRes {
let mut used = HashSet::default(); let mut used = HashSet::default();
let mut vars = HashSet::default(); let mut vars = HashSet::default();
@@ -218,9 +218,9 @@ impl<'a> Encoder<'a> {
vars.insert(var); vars.insert(var);
} }
} }
let overlap = used.intersection(&self.vars.active.values()); let overlap = used.intersection(&self.vars.values());
for var in &vars { for var in &vars {
if let Some(reg) = self.vars.active.get(var) if let Some(reg) = self.vars.get(var)
&& used.contains(reg) && used.contains(reg)
{} {}
} }
@@ -247,20 +247,8 @@ impl<'a> Encoder<'a> {
code: Code::default(), code: Code::default(),
sym_tab: SymTable::new(program.sym_count()), sym_tab: SymTable::new(program.sym_count()),
sym_refs: Default::default(), sym_refs: Default::default(),
vars: VarMap::default(), segs: Default::default(),
program, program,
} }
} }
} }
pub struct VarMap {
active: HashMap<VarId, Reg>,
}
impl Default for VarMap {
fn default() -> Self {
Self {
active: Default::default(),
}
}
}
+12 -5
View File
@@ -11,9 +11,10 @@ pub struct Code {
} }
impl Code { impl Code {
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes { pub fn mov(&mut self, dst: impl Into<RegMemKind>, src: impl Into<RegMemImm>) -> EncodeRes {
let src = src.into(); let src = src.into();
match dst.kind() { let dst = dst.into();
match dst {
RegMemKind::Reg(mut dst) => match src { RegMemKind::Reg(mut dst) => match src {
RegMemImm::Reg(src) => { RegMemImm::Reg(src) => {
if dst.width() != src.width() { if dst.width() != src.width() {
@@ -162,7 +163,13 @@ impl Code {
self.bytes.push(0xc3); self.bytes.push(0xc3);
} }
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> EncodeRes { fn add_sub(
&mut self,
dst: impl Into<RegMemKind>,
src: impl Into<RegMemImm>,
ext: u8,
) -> EncodeRes {
let dst = dst.into();
match src.into() { match src.into() {
RegMemImm::Reg(src) => { RegMemImm::Reg(src) => {
if src.width() != dst.width() { if src.width() != dst.width() {
@@ -219,11 +226,11 @@ impl Code {
Ok(()) Ok(())
} }
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes { pub fn add(&mut self, dst: impl Into<RegMemKind>, src: impl Into<RegMemImm>) -> EncodeRes {
self.add_sub(dst, src, 0) self.add_sub(dst, src, 0)
} }
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes { pub fn sub(&mut self, dst: impl Into<RegMemKind>, src: impl Into<RegMemImm>) -> EncodeRes {
self.add_sub(dst, src, 5) self.add_sub(dst, src, 5)
} }
+6 -11
View File
@@ -1,11 +1,6 @@
use super::*; use super::*;
use crate::backend::Symbol; use crate::backend::Symbol;
pub trait RegMem: RexBit + RexW + ModRMRM + Copy + MaybeMem {
fn width(&self) -> Width;
fn kind(self) -> RegMemKind;
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum RegMemKind { pub enum RegMemKind {
Reg(RegW), Reg(RegW),
@@ -23,12 +18,12 @@ pub trait MaybeMem {
fn mem(&self) -> Option<Mem>; fn mem(&self) -> Option<Mem>;
} }
impl RegMem for RegW { impl RegMemKind {
fn width(&self) -> Width { pub fn width(&self) -> Width {
self.width() match self {
} RegMemKind::Reg(reg) => reg.width(),
fn kind(self) -> RegMemKind { RegMemKind::Mem(mem) => todo!(),
RegMemKind::Reg(self) }
} }
} }