Skip to content

Commit

Permalink
[x64] implement working prolog and epilog to include the ret instruct…
Browse files Browse the repository at this point in the history
…ion and be stack safe
  • Loading branch information
Cr0a3 committed Jul 15, 2024
1 parent 698aeb9 commit ec6b5b9
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 15 deletions.
55 changes: 51 additions & 4 deletions src/Target/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ pub(crate) trait Reg: Display + ToString + Debug {
fn from(&self, string: String) -> Box<dyn Reg>;
}

impl PartialEq for dyn Reg {
fn eq(&self, other: &Self) -> bool {
other.sub64() == self.sub64()
}
}

#[derive(Debug)]
pub(crate) struct BackendInfos {
pub(crate) varsStorage: HashMap<Var, VarStorage>,
Expand All @@ -24,6 +30,8 @@ pub(crate) struct BackendInfos {
pub(crate) openUsableRegisters32: VecDeque<Box<dyn Reg>>,
pub(crate) openUsableRegisters16: VecDeque<Box<dyn Reg>>,
pub(crate) openUsableRegisters8: VecDeque<Box<dyn Reg>>,
pub(crate) saveRegister: Vec<Box<dyn Reg>>,
pub(crate) savedRegisters: Vec<Box<dyn Reg>>,
}

impl BackendInfos {
Expand All @@ -35,6 +43,9 @@ impl BackendInfos {
openUsableRegisters32: VecDeque::new(),
openUsableRegisters16: VecDeque::new(),
openUsableRegisters8: VecDeque::new(),

saveRegister: vec![],
savedRegisters: vec![],
}
}

Expand Down Expand Up @@ -67,6 +78,14 @@ impl BackendInfos {
}
}

/// Adds the register back to the usable registers in the front
pub(crate) fn dropReg(&mut self, reg: Box<dyn Reg>) {
self.openUsableRegisters64.push_front(reg.from(reg.sub64()));
self.openUsableRegisters32.push_front(reg.from(reg.sub32()));
self.openUsableRegisters16.push_front(reg.from(reg.sub16()));
self.openUsableRegisters8.push_front(reg.from(reg.sub8()));
}

pub(crate) fn insertVar(&mut self, var: Var, store: VarStorage) {
self.varsStorage.insert(var, store);
}
Expand All @@ -75,29 +94,57 @@ impl BackendInfos {
self.openUsableRegisters32.pop_front(); // update all other members
self.openUsableRegisters16.pop_front();
self.openUsableRegisters8.pop_front();
self.openUsableRegisters64.pop_front()
let reg = self.openUsableRegisters64.pop_front()?;

if self.savedRegisters.contains(&reg) && !self.saveRegister.contains(&reg) {
self.savedRegisters.push(reg.boxed());
}


Some(reg)
}

pub(crate) fn getOpenReg32(&mut self) -> Option<Box<dyn Reg>> {
self.openUsableRegisters64.pop_front(); // update all other members
self.openUsableRegisters16.pop_front();
self.openUsableRegisters8.pop_front();
self.openUsableRegisters32.pop_front()
let reg = self.openUsableRegisters32.pop_front()?;

if self.savedRegisters.contains(&reg) && !self.saveRegister.contains(&reg) {
self.savedRegisters.push(reg.boxed());
}


Some(reg)
}

pub(crate) fn getOpenReg16(&mut self) -> Option<Box<dyn Reg>> {
self.openUsableRegisters64.pop_front(); // update all other members
self.openUsableRegisters32.pop_front();
self.openUsableRegisters8.pop_front();
self.openUsableRegisters16.pop_front()
let reg = self.openUsableRegisters16.pop_front()?;

if self.savedRegisters.contains(&reg) && !self.saveRegister.contains(&reg) {
self.savedRegisters.push(reg.boxed());
}


Some(reg)
}

#[allow(dead_code)]
pub(crate) fn getOpenReg8(&mut self) -> Option<Box<dyn Reg>> {
self.openUsableRegisters64.pop_front(); // update all other members
self.openUsableRegisters32.pop_front();
self.openUsableRegisters16.pop_front();
self.openUsableRegisters8.pop_front()
let reg = self.openUsableRegisters8.pop_front()?;

if self.savedRegisters.contains(&reg) && !self.saveRegister.contains(&reg) {
self.savedRegisters.push(reg.boxed());
}


Some(reg)
}

pub(crate) fn getOpenRegBasedOnTy(&mut self, ty: TypeMetadata) -> Option<Box<dyn Reg>> {
Expand Down
80 changes: 69 additions & 11 deletions src/Target/x64/ir.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::VecDeque;

use crate::{prelude::{Block, Function, Type, TypeMetadata, Var}, Target::{registry::{Reg, VarStorage}, TargetBackendDescr}, IR::ir::*};

use crate::Target::CallConv;
Expand Down Expand Up @@ -155,12 +157,16 @@ pub(crate) fn CompileAddTyTy(add: &Add<Type, Type, Var>, registry: &mut TargetBa
}

pub(crate) fn CompileRetType(ret: &Return<Type>, registry: &mut TargetBackendDescr) -> Vec<String> {
vec![format!("mov {}, {}", match ret.inner1 {
Type::u16(_) | Type::i16(_) => registry.call.ret16(),
Type::u32(_) | Type::i32(_) => registry.call.ret32(),
Type::u64(_) | Type::i64(_) => registry.call.ret64(),
Type::Void => todo!(),
}, ret.inner1.val())]
if ret.inner1 != Type::Void {
vec![format!("mov {}, {}", match ret.inner1 {
Type::u16(_) | Type::i16(_) => registry.call.ret16(),
Type::u32(_) | Type::i32(_) => registry.call.ret32(),
Type::u64(_) | Type::i64(_) => registry.call.ret64(),
Type::Void => todo!(),
}, ret.inner1.val())]
} else {
vec![]
}
}


Expand All @@ -186,6 +192,40 @@ pub(crate) fn CompileRetVar(ret: &Return<Var>, registry: &mut TargetBackendDescr
else { unreachable!() }
})]
}

pub(crate) fn x64BuildProlog(_: &Block, registry: &mut TargetBackendDescr) -> Vec<String> {
let mut res = vec![];

if registry.backend.currStackOffsetForLocalVars != 0 {
res.push(format!("push rbp"));
res.push(format!("mov rbp, rsp"));
res.push(format!("sub rsp, 16"));
}

for backuped in &registry.backend.saveRegister {
res.push( format!("push {}", backuped) )
}

res
}

pub(crate) fn x64BuildEpilog(_: &Block, registry: &mut TargetBackendDescr) -> Vec<String> {
let mut res = vec![];

for backuped in &registry.backend.saveRegister {
res.push( format!("pop {}", backuped) )
}

if registry.backend.currStackOffsetForLocalVars != 0 {
res.push(format!("add rsp, 16"));
res.push(format!("pop rbp"));
}

res.push(format!("ret"));

res
}

impl Block {
/// Builds the block to x86 assembly intel syntax
pub fn buildAsmX86<'a>(&'a self, func: &Function, call: &CallConv, registry: &mut TargetBackendDescr<'a>) -> Vec<String> {
Expand All @@ -195,10 +235,11 @@ impl Block {

let mut reg_vars = 0;
let mut stack_off = 0;
let mut var_index = 0;

for (index, meta) in &func.ty.args {
for (_, meta) in &func.ty.args {
let mut var = Var(&mut self.clone(), meta.to_owned());
var.name = format!("%{}", index);
var.name = format!("%{}", var_index);

info.insertVar(var, {
if reg_vars >= call.regArgs() {
Expand All @@ -221,19 +262,36 @@ impl Block {
})
}
});

var_index += 1;
}

if reg_vars < call.regArgs() {
info.dropReg(call.args64()[reg_vars - 1].boxed());
}

let mut out = vec![];
let mut out = VecDeque::new();

for node in &self.nodes {
let compiled = node.compile(registry);

out.extend_from_slice(&compiled);
out.extend(compiled);
}



registry.block = None;

out
let mut prolog = x64BuildProlog(&self, registry);
prolog.reverse(); // cuz: push_front

for epAsm in prolog {
out.push_front(epAsm);
}

out.extend(x64BuildEpilog(&self, registry));

Vec::from(out)
}

pub(crate) fn isVarUsedAfterNode(&self, startingNode: &Box<dyn Ir>, var: &Var) -> bool {
Expand Down
4 changes: 4 additions & 0 deletions src/Target/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub(crate) mod call;
pub fn initializeX64Target<'a>(call_conv: CallConv) -> TargetBackendDescr<'a> {
let mut target = TargetBackendDescr::new();

target.backend.savedRegisters = vec![
x64Reg::R10.boxed(), x64Reg::R11.boxed(), x64Reg::R12.boxed(), x64Reg::R13.boxed(), x64Reg::R14.boxed(), x64Reg::R15.boxed(),
];

match call_conv {
CallConv::WindowsFastCall => {
target.backend.openUsableRegisters64 = VecDeque::from(
Expand Down

0 comments on commit ec6b5b9

Please sign in to comment.