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(),
}
}
}
+9 -9
View File
@@ -1,7 +1,7 @@
use super::*;
use crate::backend::Symbol;
type ERes = Result<(), CompilerMsg>;
pub type EncodeRes = Result<(), CompilerMsg>;
/// machine code
#[derive(Default)]
@@ -11,7 +11,7 @@ pub struct Code {
}
impl Code {
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
pub fn mov(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
let src = src.into();
match dst.kind() {
RegMemKind::Reg(mut dst) => match src {
@@ -92,7 +92,7 @@ impl Code {
Ok(())
}
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> ERes {
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> EncodeRes {
match reg.into() {
RegMemImm::Reg(reg) => match reg.width() {
Width::B64 => {
@@ -120,7 +120,7 @@ impl Code {
Ok(())
}
pub fn pop(&mut self, reg: Reg) -> ERes {
pub fn pop(&mut self, reg: RegW) -> EncodeRes {
match reg.width() {
Width::B64 | Width::B16 => (),
_ => return Err("register must be 64 or 16 bit".into()),
@@ -133,7 +133,7 @@ impl Code {
Ok(())
}
pub fn lea(&mut self, dst: Reg, sym: Symbol) -> ERes {
pub fn lea(&mut self, dst: RegW, sym: Symbol) -> EncodeRes {
self.rex(1, dst, 0, 0)?;
self.bytes.push(0x8d);
self.modrm(dst, sym);
@@ -162,7 +162,7 @@ impl Code {
self.bytes.push(0xc3);
}
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> ERes {
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> EncodeRes {
match src.into() {
RegMemImm::Reg(src) => {
if src.width() != dst.width() {
@@ -219,11 +219,11 @@ impl Code {
Ok(())
}
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
self.add_sub(dst, src, 0)
}
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> EncodeRes {
self.add_sub(dst, src, 5)
}
@@ -245,7 +245,7 @@ impl Code {
Ok(())
}
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> ERes {
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> EncodeRes {
if r.req() && b.req_no() || r.req_no() && b.req() {
return Err("registers incompatible (REX)".into());
}
+7 -1
View File
@@ -20,9 +20,15 @@ pub struct X86_64;
impl Arch for X86_64 {
const NAME: &str = "x86_64";
type Asm = Code;
type Asm = Asm;
type Addr = u64;
type CallConv = CallConv;
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> {
compile(p)
}
}
pub struct CallConv {
volatile: Vec<Reg>,
nonvolatile: Vec<Reg>,
}
+3 -3
View File
@@ -36,13 +36,13 @@ pub fn imms() -> impl Iterator<Item = i128> {
IMMS.iter().cloned()
}
pub fn regs() -> impl Iterator<Item = Reg> {
Reg::IMPORTANT.iter().cloned()
pub fn regs() -> impl Iterator<Item = RegW> {
RegW::IMPORTANT.iter().cloned()
}
pub fn mems() -> impl Iterator<Item = Mem> {
gen move {
for &reg in Reg::IMPORTANT {
for &reg in RegW::IMPORTANT {
for &disp in DISPS {
for &width in WIDTHS {
yield mem(reg, disp, width);
+12 -12
View File
@@ -8,13 +8,13 @@ pub trait RegMem: RexBit + RexW + ModRMRM + Copy + MaybeMem {
#[derive(Clone, Copy)]
pub enum RegMemKind {
Reg(Reg),
Reg(RegW),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum RegMemImm {
Reg(Reg),
Reg(RegW),
Imm(Imm),
Mem(Mem),
}
@@ -23,7 +23,7 @@ pub trait MaybeMem {
fn mem(&self) -> Option<Mem>;
}
impl RegMem for Reg {
impl RegMem for RegW {
fn width(&self) -> Width {
self.width()
}
@@ -32,7 +32,7 @@ impl RegMem for Reg {
}
}
impl MaybeMem for Reg {
impl MaybeMem for RegW {
fn mem(&self) -> Option<Mem> {
None
}
@@ -54,14 +54,14 @@ impl MaybeMem for Mem {
}
// fromrot
impl From<Reg> for RegMemImm {
fn from(value: Reg) -> Self {
impl From<RegW> for RegMemImm {
fn from(value: RegW) -> Self {
Self::Reg(value)
}
}
impl From<Reg> for RegMemKind {
fn from(value: Reg) -> Self {
impl From<RegW> for RegMemKind {
fn from(value: RegW) -> Self {
Self::Reg(value)
}
}
@@ -115,7 +115,7 @@ pub enum EffAddr {
None,
}
impl ModRMRM for Reg {
impl ModRMRM for RegW {
fn rm(&self) -> u8 {
self.base()
}
@@ -171,7 +171,7 @@ impl ModRMReg for u8 {
}
}
impl ModRMReg for Reg {
impl ModRMReg for RegW {
fn val(&self) -> u8 {
self.base()
}
@@ -207,7 +207,7 @@ impl RexBit for u8 {
}
}
impl RexBit for Reg {
impl RexBit for RegW {
fn rex(&self) -> bool {
self.gt8()
}
@@ -238,7 +238,7 @@ impl RexW for Width {
}
}
impl RexW for Reg {
impl RexW for RegW {
fn rexw(&self) -> bool {
self.width().rexw()
}
+128
View File
@@ -0,0 +1,128 @@
use super::*;
use crate::backend::{Symbol, VarId};
pub struct Asm {
pub args: Vec<(Reg, VarId)>,
pub instrs: Vec<Instr>,
}
pub enum Instr {
Mov { dst: Rvm, src: Rvmi },
Push(Rvmi),
Pop(Rvm),
Lea { dst: Rv, src: Symbol },
Int(u8),
Syscall,
Call(Symbol),
CallAt(Symbol),
Ret,
Add { dst: Rvm, src: Rvmi },
Sub { dst: Rvm, src: Rvmi },
}
impl Instr {
pub fn regw_set(&self) -> Option<RegW> {
match self {
Instr::Mov { dst, src: _ } => dst.reg(),
Instr::Push(_) => None,
Instr::Pop(dst) => dst.reg(),
Instr::Lea { dst, src: _ } => dst.reg(),
Instr::Int(_) => None,
Instr::Syscall => None,
Instr::Call(_) => None,
Instr::CallAt(_) => None,
Instr::Ret => None,
Instr::Add { dst, src: _ } => dst.reg(),
Instr::Sub { dst, src: _ } => dst.reg(),
}
}
pub fn vars(&self) -> impl Iterator<Item = VarId> {
let mut vars = (None, None);
match self {
Instr::Mov { dst, src } => vars = (dst.var(), src.var()),
Instr::Push(src) => vars.0 = src.var(),
Instr::Pop(dst) => vars.0 = dst.var(),
Instr::Lea { dst, .. } => vars.0 = dst.var(),
Instr::Int(_) => (),
Instr::Syscall => (),
Instr::Call(_) => (),
Instr::CallAt(_) => (),
Instr::Ret => (),
Instr::Add { dst, src } => vars = (dst.var(), src.var()),
Instr::Sub { dst, src } => vars = (dst.var(), src.var()),
}
vars.0.into_iter().chain(vars.1)
}
}
#[derive(Clone, Copy)]
pub enum Rv {
Reg(RegW),
Var(VarId),
}
#[derive(Clone, Copy)]
pub enum Rvm {
Reg(RegW),
Var(VarId),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum Rvmi {
Reg(RegW),
Var(VarId),
Mem(Mem),
Imm(Imm),
}
impl Rv {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
impl Rvm {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
impl Rvmi {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
+2 -2
View File
@@ -4,12 +4,12 @@ use super::*;
#[derive(Clone, Copy)]
pub struct Mem {
pub reg: Reg,
pub reg: RegW,
pub disp: i32,
pub width: Width,
}
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
pub fn mem(reg: RegW, disp: i32, width: Width) -> Mem {
Mem { reg, disp, width }
}
+2
View File
@@ -1,10 +1,12 @@
mod arg;
mod asm;
mod imm;
mod mem;
mod reg;
mod width;
pub use arg::*;
pub use asm::*;
pub use imm::*;
pub use mem::*;
pub use reg::*;
+26 -20
View File
@@ -1,13 +1,15 @@
use super::Width;
#[derive(Clone, Copy, PartialEq)]
pub struct Reg {
val: u8,
pub struct RegW {
reg: Reg,
high: bool,
width: Width,
}
def_regs! {
pub type Reg = u8;
def_regs! { RegW;
0b0000 : rax eax ax al,
0b0001 : rcx ecx cx cl !_,
0b0010 : rdx edx dx dl,
@@ -28,16 +30,20 @@ def_regs! {
0b1111 : r15 r15d r15w r15b,
}
impl Reg {
impl RegW {
pub fn reg(&self) -> u8 {
self.reg
}
pub fn base(&self) -> u8 {
self.val & 0b111
self.reg & 0b111
}
/// checks if register is not one of the first 8 (0-7)
pub fn gt8(&self) -> bool {
self.val >= 0b1000
self.reg >= 0b1000
}
pub fn gt4(&self) -> bool {
self.val >= 0b0100
self.reg >= 0b0100
}
pub fn width(&self) -> Width {
@@ -65,12 +71,12 @@ impl Reg {
|| (self.gt4() && self.width == Width::B8 && !self.high)
}
pub fn incompatible(&self, other: &Reg) -> bool {
pub fn incompatible(&self, other: &RegW) -> bool {
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
}
const fn new(val: u8, width: Width, high: bool) -> Self {
Self { val, high, width }
const fn new(reg: u8, width: Width, high: bool) -> Self {
Self { reg, high, width }
}
}
@@ -117,30 +123,30 @@ macro_rules! filter {
use filter;
macro_rules! def_regs {
($($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
($reg:ident; $($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
$(
#[allow(non_upper_case_globals)]
pub const $B64: Reg = Reg::new($val, Width::B64, false);
pub const $B64: $reg = $reg::new($val, Width::B64, false);
#[allow(non_upper_case_globals)]
pub const $B32: Reg = Reg::new($val, Width::B32, false);
pub const $B32: $reg = $reg::new($val, Width::B32, false);
#[allow(non_upper_case_globals)]
pub const $B16: Reg = Reg::new($val, Width::B16, false);
pub const $B16: $reg = $reg::new($val, Width::B16, false);
#[allow(non_upper_case_globals)]
pub const $B8 : Reg = Reg::new($val, Width::B8 , false);
pub const $B8 : $reg = $reg::new($val, Width::B8 , false);
$(
#[allow(non_upper_case_globals)]
pub const $B8H: Reg = Reg::new($val, Width::B8, true);
pub const $B8H: $reg = $reg::new($val, Width::B8, true);
)?
)*
impl Reg {
impl $reg {
// #[cfg(test)]
// pub const ALL: &[Reg] = &[
// pub const ALL: &[$reg] = &[
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
// ];
#[cfg(test)]
pub const IMPORTANT: &[Reg] = &
pub const IMPORTANT: &[$reg] = &
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
;
@@ -159,7 +165,7 @@ macro_rules! def_regs {
})
}
}
impl std::fmt::Display for Reg {
impl std::fmt::Display for $reg {
#[allow(non_upper_case_globals)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self {
+2 -2
View File
@@ -9,8 +9,8 @@ pub enum Width {
B64 = 3,
}
impl From<Reg> for Width {
fn from(value: Reg) -> Self {
impl From<RegW> for Width {
fn from(value: RegW) -> Self {
value.width()
}
}