work
This commit is contained in:
+138
-150
@@ -1,14 +1,15 @@
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
||||
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 code: Code,
|
||||
pub sym_tab: SymTable<u64>,
|
||||
pub sym_refs: HashMap<Symbol, Vec<usize>>,
|
||||
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> {
|
||||
@@ -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);
|
||||
|
||||
for f in &p.funcs {
|
||||
let addr = encoder.code.bytes.len();
|
||||
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)?;
|
||||
}
|
||||
encoder.func(f);
|
||||
}
|
||||
|
||||
for (pos, sym) in encoder.code.missing.drain(..) {
|
||||
@@ -80,133 +70,143 @@ pub struct RegUse {
|
||||
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 ® 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(®) = 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(®);
|
||||
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>;
|
||||
impl<'a> Encoder<'a> {
|
||||
fn compile_instr(&mut self, instr: &BInstr) -> EncodeRes {
|
||||
match instr {
|
||||
BInstr::Asm(asm) => self.asm(asm)?,
|
||||
_ => todo!(),
|
||||
fn func(&mut self, f: &Func<X86_64>) -> EncodeRes {
|
||||
let addr = self.code.bytes.len();
|
||||
self.sym_tab.insert(f.sym, addr as u64);
|
||||
self.segs.clear();
|
||||
self.calc_uses(&f.body);
|
||||
for instr in &f.body {
|
||||
match instr {
|
||||
BInstr::Asm(asm) => self.asm(asm)?,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
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 ® 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(®) = 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(®);
|
||||
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 {
|
||||
let mut used = HashSet::default();
|
||||
let mut vars = HashSet::default();
|
||||
@@ -218,9 +218,9 @@ impl<'a> Encoder<'a> {
|
||||
vars.insert(var);
|
||||
}
|
||||
}
|
||||
let overlap = used.intersection(&self.vars.active.values());
|
||||
let overlap = used.intersection(&self.vars.values());
|
||||
for var in &vars {
|
||||
if let Some(reg) = self.vars.active.get(var)
|
||||
if let Some(reg) = self.vars.get(var)
|
||||
&& used.contains(reg)
|
||||
{}
|
||||
}
|
||||
@@ -247,20 +247,8 @@ impl<'a> Encoder<'a> {
|
||||
code: Code::default(),
|
||||
sym_tab: SymTable::new(program.sym_count()),
|
||||
sym_refs: Default::default(),
|
||||
vars: VarMap::default(),
|
||||
segs: Default::default(),
|
||||
program,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VarMap {
|
||||
active: HashMap<VarId, Reg>,
|
||||
}
|
||||
|
||||
impl Default for VarMap {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user