Skip to content

Commit

Permalink
[X64] Starting x64 backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Cr0a3 committed Jul 13, 2024
1 parent 64b69c9 commit 1381f4e
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 11 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ edition = "2021"

[dependencies]
object = { version = "0.36.1", features = ["write"] }
once_cell = "1.19.0"
18 changes: 13 additions & 5 deletions examples/simple.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::error::Error;
use Ygen::{prelude::*, PassManager::Passes::PreComputeValue};
use Ygen::{prelude::*, PassManager::Passes::PreComputeValue, Target::{initializeX64Target, CallConv}};

pub fn main() -> Result<(), Box<dyn Error>> {

initializeX64Target();

let mut module = Module();

let mut builder = IRBuilder();

let ty = FnTy(vec![TypeMetadata::i32, TypeMetadata::i32], TypeMetadata::i32);
Expand All @@ -14,10 +18,12 @@ pub fn main() -> Result<(), Box<dyn Error>> {
builder.positionAtEnd(entry);

let val = builder.BuildAdd(ty.arg(0), ty.arg(1));
let add2 = builder.BuildAdd(Type::i32(5), Type::i32(5));
let ret = builder.BuildAdd(val, add2);
//let add2 = builder.BuildAdd(Type::i32(5), Type::i32(5));
//let ret = builder.BuildAdd(val, add2);

builder.BuildRet( ret );
//builder.BuildRet( ret );
let block = builder.getLastBlock().clone().unwrap().clone();
let func = func.clone().to_owned().clone();

module.verify().print();

Expand All @@ -26,9 +32,11 @@ pub fn main() -> Result<(), Box<dyn Error>> {

module.runPassMngr(passes);

println!("{}",
eprintln!("{}",
module.dumpColored()
);

eprintln!("{:#?}", block.buildAsmX86(&func, &CallConv::WindowsFastCall));

Ok(())
}
5 changes: 5 additions & 0 deletions src/IR/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ impl<'a> IRBuilder<'a> {
self.blocks.push_front(block);
self.curr = 0; // Can cause an intenger underflow but shouldn't
}

/// Returns the last block of the builder
pub fn getLastBlock(&mut self) -> Option<&Block> {
Some(self.blocks.back()?.to_owned().to_owned())
}
}

/// Creates an new IRBuilder
Expand Down
2 changes: 1 addition & 1 deletion src/IR/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ impl BuildAdd<Var, Var> for IRBuilder<'_> {

let op0Ty: TypeMetadata = op0.ty.into();

let ty = op0Ty; // now both types need to be the same
let ty = op0Ty;
let var = Var::new(block, ty);

block.push_ir(Add::new(op0, op1, var.clone()));
Expand Down
2 changes: 0 additions & 2 deletions src/IR/typ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::fmt::Display;
///
/// If you want an empty Type consider using `TypeMetadata`
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(non_camel_case_types)]
pub enum Type {
/// Just an u16 with a value
u16(u16),
Expand All @@ -26,7 +25,6 @@ pub enum Type {

/// Stores type metadata (just the type without data)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
pub enum TypeMetadata {
/// u16
u16,
Expand Down
2 changes: 1 addition & 1 deletion src/Support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl PrintErrorAndExit for Result<(), VerifyError> {
match self {
Ok(_) => {},
Err(e) => {
println!("{}", e);
eprintln!("{}", e);
exit(-1)
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Target/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod triple;
mod registry;
mod x64;
pub use x64::initializeX64Target;
pub use triple::Triple;
pub use registry::TargetRegistry;

Expand Down
8 changes: 7 additions & 1 deletion src/Target/registry.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use std::sync::Mutex;

use once_cell::sync::OnceCell;

use super::Arch;

/// The Target Registry: stores if a target was already initialized
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TargetRegistry {
inited_targets: Vec<Arch>,
pub(crate) inited_targets: Vec<Arch>,
}

pub(crate) static TARGETS: OnceCell<Mutex<TargetRegistry>> = OnceCell::new();

impl TargetRegistry {
/// Creates a new instance
pub fn new() -> Self {
Expand Down
35 changes: 35 additions & 0 deletions src/Target/x64/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::Target::CallConv;

impl CallConv {
/// Returns the number of register arguments in the calling convention
pub fn regArgs(&self) -> usize {
match self {
CallConv::SystemV => 6,
CallConv::WindowsFastCall => 4,
}
}

/// Returns the 16Bit intenger argument registers as a vec
pub fn args16(&self) -> Vec<String> {
match self {
CallConv::SystemV => vec!["si".into(), "di".into(), "dx".into(), "cx".into(), "r8w".into(), "r9w".into()],
CallConv::WindowsFastCall => vec!["dx".into(), "cx".into(), "r8w".into(), "r9w".into()],
}
}

/// Returns the 32Bit intenger argument registers as a vec
pub fn args32(&self) -> Vec<String> {
match self {
CallConv::SystemV => vec!["esi".into(), "edi".into(), "edx".into(), "ecx".into(), "r8d".into(), "r9d".into()],
CallConv::WindowsFastCall => vec!["edx".into(), "ecx".into(), "r8d".into(), "r9d".into()],
}
}

/// Returns the 16Bit intenger argument registers as a vec
pub fn args64(&self) -> Vec<String> {
match self {
CallConv::SystemV => vec!["rsi".into(), "rdi".into(), "rdx".into(), "rcx".into(), "r8".into(), "r9".into()],
CallConv::WindowsFastCall => vec!["rdx".into(), "rcx".into(), "r8".into(), "r9".into()],
}
}
}
125 changes: 125 additions & 0 deletions src/Target/x64/ir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::{any::Any, collections::HashMap};

use crate::{prelude::{Block, Function, TypeMetadata, Var}, IR::ir::*};

use crate::Target::CallConv;

/// Stores compilation infos for ir node compilation
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct x64CompilationInfos {
pub(crate) varsStorage: HashMap<Var, VarStorage>,
}

impl x64CompilationInfos {
pub(crate) fn insertVar(&mut self, var: Var, store: VarStorage) {
self.varsStorage.insert(var, store);
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum VarStorage {
Register(String),
Memory(String),
}

/// A trait which is used to implement compilability for ir nodes
pub(crate) trait Compile: Ir {
/// Compiles the node into an asm string
fn compile(&self, infos: &mut x64CompilationInfos) -> Vec<String>;
}

impl Compile for Add<Var, Var, Var> {
fn compile(&self, infos: &mut x64CompilationInfos) -> Vec<String> {
let loc1 = if let Some(loc1) = infos.varsStorage.get(&self.inner1) {
loc1.clone()
} else {
panic!("unknown variable: {:?}", self.inner1)
};

let loc2 = if let Some(loc2) = infos.varsStorage.get(&self.inner2) {
loc2.clone()

} else {
panic!("unknown variable: {:?}", self.inner1)
};

let op0 = if let VarStorage::Register(ref reg) = loc1 {
reg.to_string()
} else if let VarStorage::Memory(ref mem) = loc1 {
mem.to_string()
} else { panic!() };

let op1 = if let VarStorage::Register(ref reg) = loc2 {
reg.to_string()
} else if let VarStorage::Memory(ref mem) = loc2 {
mem.to_string()
} else { panic!() };

let ret: String = "rax".into();

infos.insertVar(
self.inner3.clone(),
VarStorage::Register(ret.clone())
);

if let VarStorage::Register(_) = loc1 {
if let VarStorage::Register(_) = loc2 {
return vec![format!("lea {}, [{} + {}", ret, op0, op1)];
}
}

if let VarStorage::Memory(_) = loc1 {
if let VarStorage::Memory(_) = loc2 {
return vec![
format!("mov rax, {}", op0),
format!("mov rbx, {}", op1),
format!("add rax, rbx"),
format!("mov rax, {}", ret),
];
}
}

vec![]
}
}

impl Block {
/// Builds the block to x86 assembly intel syntax
pub fn buildAsmX86(&self, func: &Function, call: &CallConv) -> Vec<String> {
let mut info = x64CompilationInfos { varsStorage: HashMap::new() };

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

for (_, meta) in &func.ty.args {
info.insertVar(Var(&mut self.clone(), *meta), {
if reg_vars >= call.regArgs() {
let addend = match meta {
TypeMetadata::u16 | TypeMetadata::i16=> 2,
TypeMetadata::u32 | TypeMetadata::i32=> 4,
TypeMetadata::u64 | TypeMetadata::i64=> 8,
TypeMetadata::Void => continue,
};

stack_off += addend;
VarStorage::Memory(format!("[rbp - {}]", stack_off - addend))
} else {
reg_vars += 1;
VarStorage::Register(format!("{}", match meta {
TypeMetadata::u16 | TypeMetadata::i16 => call.args16()[reg_vars - 1].clone(),
TypeMetadata::u32 | TypeMetadata::i32 => call.args32()[reg_vars - 1].clone(),
TypeMetadata::u64 | TypeMetadata::i64 => call.args64()[reg_vars - 1].clone(),
TypeMetadata::Void => continue,
}))
}
});
}

for node in &self.nodes {
let ty = (node.as_any()).downcast_ref::<Box<dyn Compile>>().unwrap();
ty.compile(&mut info);
}

vec![]
}
}
17 changes: 17 additions & 0 deletions src/Target/x64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! The x64 Target: used for compiling ir and inline asm into x64 machine code

use std::sync::Mutex;

use super::{registry::TARGETS, Arch, TargetRegistry};

pub(crate) mod ir;
pub(crate) mod call;

/// Initializes the x86-64 target
pub fn initializeX64Target() {
TARGETS.get_or_init( || {
Mutex::new(TargetRegistry::new())
});

TARGETS.get().unwrap().lock().unwrap().set_inited(Arch::X86_64);
}
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#![warn(unreachable_pub)]
#![allow(clippy::redundant_field_names)]
#![allow(rustdoc::invalid_html_tags)]
#![allow(rustdoc::invalid_codeblock_attributes)]
#![allow(non_camel_case_types)]

//! # Ygen - <b>Y</b>et another Code <b>Gen</b>erator
//! ### Description
Expand All @@ -22,7 +24,7 @@
//!
//! ###### Here is a quick introduction to the YGEN-IR:
//! A function is defined like this:
//! ```
//! ```no-run
//! define i32 @add( i32 %0, i32 %1 ) {
//! entry:
//! %2 = add i32 %0, %1
Expand Down

0 comments on commit 1381f4e

Please sign in to comment.