From 9c395c424edece0a06a652dd3ae9d67294be1a77 Mon Sep 17 00:00:00 2001 From: TheCPP Date: Fri, 23 Aug 2024 16:33:55 +0200 Subject: [PATCH] [STYLE] moving code into their own sub files --- src/IR/ir.rs | 793 --------------------------- src/IR/mod.rs | 5 +- src/IR/nodes/assign.rs | 164 ++++++ src/IR/nodes/call.rs | 103 ++++ src/IR/nodes/cast.rs | 63 +++ src/IR/nodes/math.rs | 217 ++++++++ src/IR/nodes/mod.rs | 165 ++++++ src/IR/nodes/ret.rs | 101 ++++ src/Target/x64/compilation/assign.rs | 152 +++++ src/Target/x64/compilation/call.rs | 120 ++++ src/Target/x64/compilation/cast.rs | 74 +++ src/Target/x64/compilation/math.rs | 225 ++++++++ src/Target/x64/compilation/mod.rs | 93 ++++ src/Target/x64/compilation/prolog.rs | 36 ++ src/Target/x64/compilation/ret.rs | 37 ++ src/Target/x64/ir.rs | 716 ------------------------ src/Target/x64/mod.rs | 5 +- 17 files changed, 1557 insertions(+), 1512 deletions(-) delete mode 100644 src/IR/ir.rs create mode 100644 src/IR/nodes/assign.rs create mode 100644 src/IR/nodes/call.rs create mode 100644 src/IR/nodes/cast.rs create mode 100644 src/IR/nodes/math.rs create mode 100644 src/IR/nodes/mod.rs create mode 100644 src/IR/nodes/ret.rs create mode 100644 src/Target/x64/compilation/assign.rs create mode 100644 src/Target/x64/compilation/call.rs create mode 100644 src/Target/x64/compilation/cast.rs create mode 100644 src/Target/x64/compilation/math.rs create mode 100644 src/Target/x64/compilation/mod.rs create mode 100644 src/Target/x64/compilation/prolog.rs create mode 100644 src/Target/x64/compilation/ret.rs delete mode 100644 src/Target/x64/ir.rs diff --git a/src/IR/ir.rs b/src/IR/ir.rs deleted file mode 100644 index 957d06e2..00000000 --- a/src/IR/ir.rs +++ /dev/null @@ -1,793 +0,0 @@ -use std::{any::Any, fmt::Debug, hash::Hash}; -use super::{Const, Function, FunctionType, IRBuilder, Type, TypeMetadata, Var, VerifyError}; -use crate::Target::{instr::Instr, TargetBackendDescr}; - -macro_rules! IrTypeWith3 { - ($name:tt, $param1:tt, $param2:tt, $param3:tt) => { - /// An Ir node - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name<$param1, $param2, $param3> { - /// first inner value - pub(crate) inner1: $param1, - /// second inner value - pub(crate) inner2: $param2, - /// third inner value - pub(crate) inner3: $param3, - } - - - impl<$param1, $param2, $param3> $name<$param1, $param2, $param3> { - /// Creates new instance - #[allow(dead_code)] - pub fn new(op0: $param1, op1: $param2, op2: $param3) -> Box { - Box::from( - Self { - inner1: op0, - inner2: op1, - inner3: op2, - } - ) - } - } - }; -} -macro_rules! IrTypeWith2 { - ($name:tt, $param1:tt, $param2:tt) => { - /// An Ir node - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name<$param1, $param2> { - /// first inner value - pub inner1: $param1, - /// second inner value - pub inner2: $param2, - } - - - impl<$param1, $param2> $name<$param1, $param2> { - /// Creates new instance - #[allow(dead_code)] - pub fn new(op0: $param1, op1: $param2) -> Box { - Box::from( - Self { - inner1: op0, - inner2: op1, - } - ) - } - } - }; -} -macro_rules! IrTypeWith1 { - ($name:tt, $param1:tt) => { - /// An Ir node - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub(crate) struct $name<$param1> { - /// inner value - pub(crate) inner1: $param1, - } - - - impl<$param1> $name<$param1> { - /// Creates new instance - #[allow(dead_code)] - pub(crate) fn new(op0: $param1) -> Box { - Box::from( - Self { - inner1: op0, - } - ) - } - } - }; -} - -IrTypeWith1!(Return, T); -IrTypeWith3!(Call, T, U, Z); -IrTypeWith2!(ConstAssign, T, U); -IrTypeWith3!(Cast, T, U, Z); -IrTypeWith3!(Add, T, U, Z); -IrTypeWith3!(Sub, T, U, Z); -IrTypeWith3!(Xor, T, U, Z); -IrTypeWith3!(Or, T, U, Z); -IrTypeWith3!(And, T, U, Z); - -use crate::Support::{ColorClass, ColorProfile}; - -macro_rules! MathIrNode { - ($name:ident, $compileFuncVarVar:ident, $compileFuncVarTy:ident, $compileFuncTyTy:ident, $buildTraitName:ident, $buildFuncName:ident, $dump:expr) => { - /// Used for overloading the build function - pub trait $buildTraitName { - /// Xors values - fn $buildFuncName(&mut self, op0: T, op1: U) -> Var; - } - - impl $buildTraitName for IRBuilder<'_> { - fn $buildFuncName(&mut self, op0: Type, op1: Type) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let op0Ty: TypeMetadata = op0.into(); - - let ty = op0Ty; // now both types need to be the same - let var = Var::new(block, ty); - - block.push_ir($name::new(op0, op1, var.clone())); - - var - } - } - - impl $buildTraitName for IRBuilder<'_> { - fn $buildFuncName(&mut self, op0: Var, op1: Var) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let op0Ty: TypeMetadata = op0.ty.into(); - - let ty = op0Ty; - let var = Var::new(block, ty); - - block.push_ir($name::new(op0, op1, var.clone())); - - var - } - } - - impl $buildTraitName for IRBuilder<'_> { - fn $buildFuncName(&mut self, op0: Var, op1: Type) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let op0Ty: TypeMetadata = op0.ty.into(); - - let ty = op0Ty; - let var = Var::new(block, ty); - - block.push_ir($name::new(op0, op1, var.clone())); - - var - } - } - - impl Ir for $name { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn dump(&self) -> String { - format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner3.ty, self.inner1.val(), self.inner2.val()) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} = {} {} {}, {}", - profile.markup(&self.inner3.name, ColorClass::Var), - profile.markup($dump, ColorClass::Instr), - profile.markup(&self.inner3.ty.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.val().to_string(), ColorClass::Value), - profile.markup(&self.inner2.val().to_string(), ColorClass::Value) - ) - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - let op0Ty: TypeMetadata = self.inner1.into(); - let op1Ty: TypeMetadata = self.inner2.into(); - let op2Ty: TypeMetadata = self.inner3.ty.into(); - - if !(op0Ty == op1Ty && op1Ty == op2Ty) { - if op0Ty != op1Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? - } else if op1Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? - } if op0Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? - } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } - } - - Ok(()) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.$compileFuncTyTy()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner3 { true } - else { false } - } - } - - impl Ir for $name { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn dump(&self) -> String { - format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner3.ty, self.inner1.name, self.inner2.name) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} = {} {} {}, {}", - profile.markup(&self.inner3.name, ColorClass::Var), - profile.markup($dump, ColorClass::Instr), - profile.markup(&self.inner3.ty.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.name.to_string(), ColorClass::Var), - profile.markup(&self.inner2.name.to_string(), ColorClass::Var) - ) - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - let op0Ty: TypeMetadata = self.inner1.ty.into(); - let op1Ty: TypeMetadata = self.inner2.ty.into(); - let op2Ty: TypeMetadata = self.inner3.ty.into(); - - if !(op0Ty == op1Ty && op1Ty == op2Ty) { - if op0Ty != op1Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? - } else if op1Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? - } if op0Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? - } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } - } - - Ok(()) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.$compileFuncVarVar()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 || *var == self.inner2 || *var == self.inner3 { true } - else { false } - } - } - - impl Ir for $name { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn dump(&self) -> String { - format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner1.ty, self.inner1.name, self.inner2.val()) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} = {} {} {}, {}", - profile.markup(&self.inner3.name, ColorClass::Var), - profile.markup($dump, ColorClass::Instr), - profile.markup(&self.inner1.ty.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.name.to_string(), ColorClass::Var), - profile.markup(&self.inner2.val().to_string(), ColorClass::Var) - ) - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - let op0Ty: TypeMetadata = self.inner1.ty.into(); - let op1Ty: TypeMetadata = self.inner3.ty.into(); - let op2Ty: TypeMetadata = self.inner2.into(); - - if !(op0Ty == op1Ty && op1Ty == op2Ty) { - if op0Ty != op1Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? - } else if op1Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? - } if op0Ty != op2Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? - } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } - } - - Ok(()) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.$compileFuncVarTy()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 || *var == self.inner3 { true } - else { false } - } - } - - }; -} - -MathIrNode!(Add, getCompileFuncForAddVarVar, getCompileFuncForAddVarType, getCompileFuncForAddTypeType, BuildAdd, BuildAdd, "add"); -MathIrNode!(Sub, getCompileFuncForSubVarVar, getCompileFuncForSubVarType, getCompileFuncForSubTypeType, BuildSub, BuildSub, "sub"); -MathIrNode!(Xor, getCompileFuncForXorVarVar, getCompileFuncForXorVarType, getCompileFuncForXorTypeType, BuildXor, BuildXor, "xor"); -MathIrNode!(Or, getCompileFuncForOrVarVar, getCompileFuncForOrVarType, getCompileFuncForOrTypeType, BuildOr, BuildOr, "or"); -MathIrNode!(And, getCompileFuncForAndVarVar, getCompileFuncForAndVarType, getCompileFuncForAndTypeType, BuildAnd, BuildAnd, "and"); - - -impl Ir for Return { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn dump(&self) -> String { - let metadata: TypeMetadata = self.inner1.into(); - format!("ret {} {}", metadata, self.inner1.val()) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - let metadata: TypeMetadata = self.inner1.into(); - format!("{} {} {}", - profile.markup("ret", ColorClass::Instr), - profile.markup(&metadata.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.val().to_string(), ColorClass::Var), - ) - } - - fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError> { - let ty: TypeMetadata = self.inner1.into(); - - if ty != FuncTy.ret { - Err(VerifyError::RetTyNotFnTy(ty, FuncTy.ret))? - } - - Ok(()) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForRetType()(self, registry) - } -} - -impl Ir for Return { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn dump(&self) -> String { - format!("ret {} {}", self.inner1.ty, self.inner1.name) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} {} {}", - profile.markup("ret", ColorClass::Instr), - profile.markup(&self.inner1.ty.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.name.to_string(), ColorClass::Var), - ) - } - - fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError> { - let ty: TypeMetadata = self.inner1.ty.into(); - - if ty != FuncTy.ret { - Err(VerifyError::RetTyNotFnTy(ty, FuncTy.ret))? - } - - Ok(()) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForRetVar()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 { true } - else { false } - } -} - -impl Ir for ConstAssign { - fn dump(&self) -> String { - let meta: TypeMetadata = self.inner2.into(); - format!("{} = {} {}", self.inner1.name, meta, self.inner2.val()) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - let meta: TypeMetadata = self.inner2.into(); - format!("{} = {} {}", - profile.markup(&self.inner1.name, ColorClass::Var), - profile.markup(&meta.to_string(), ColorClass::Instr), - profile.markup(&self.inner2.val().to_string(), ColorClass::Value), - ) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - let op0Ty = self.inner1.ty; - let op1Ty = self.inner2.into(); - if op0Ty != op1Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? - } - - Ok(()) - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForConstAssign()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 { true } - else { false } - } -} - -impl Ir for ConstAssign { - fn dump(&self) -> String { - let meta: TypeMetadata = self.inner2.ty; - format!("{} = {} {}", self.inner1.name, meta, self.inner2.name) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - let meta: TypeMetadata = self.inner2.ty; - format!("{} = {} {}", - profile.markup(&self.inner1.name, ColorClass::Var), - profile.markup(&meta.to_string(), ColorClass::Instr), - profile.markup(&self.inner2.name.to_string(), ColorClass::Value), - ) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - let op0Ty = self.inner1.ty; - let op1Ty = self.inner2.ty; - if op0Ty != op1Ty { - Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? - } - - Ok(()) - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForConstAssignVar()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 { true } - else if *var == self.inner2 { true } - else { false } - } -} - -impl Ir for ConstAssign { - fn dump(&self) -> String { - format!("{} = ptr {}", self.inner1.name, self.inner2.name) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} = {} {}", - profile.markup(&self.inner1.name, ColorClass::Var), - profile.markup("ptr", ColorClass::Ty), - profile.markup(&self.inner2.name.to_string(), ColorClass::Value), - ) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - Ok(()) - } - - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForConstAssignConst()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - if *var == self.inner1 { true } - else { false } - } -} - -impl Ir for Cast { - fn dump(&self) -> String { - format!("{} = cast {} to {}", self.inner3.name, self.inner1.name, self.inner2) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - format!("{} = {} {} {} {}", - profile.markup(&self.inner3.name, ColorClass::Var), - profile.markup(&"cast", ColorClass::Instr), - profile.markup(&self.inner1.name, ColorClass::Var), - profile.markup(&"to", ColorClass::Instr), - profile.markup(&self.inner2.to_string(), ColorClass::Ty), - ) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - if self.inner3.ty != self.inner2 { - Err(VerifyError::Op0Op1TyNoMatch(self.inner3.ty, self.inner2))? - } - Ok(()) - } - - fn uses(&self, var: &Var) -> bool { - let var = var.to_owned(); - - if var == self.inner1 || var == self.inner3 { - true - } else { false } - } - - fn clone_box(&self) -> Box { - Box::from( self.clone() ) - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForCastTyVar()(self, registry) - } -} - -impl Ir for Call, Var> { - fn dump(&self) -> String { - let mut fmt = String::new(); - - for arg in &self.inner2 { - fmt.push_str(&format!("{} ", arg)) - } - - format!("{} = call {} {} {}", self.inner3.name, self.inner1.ty.ret, self.inner1.name, fmt) - } - - fn dumpColored(&self, profile: ColorProfile) -> String { - let mut fmt = String::new(); - - for arg in &self.inner2 { - fmt.push_str(&arg.to_colored_string(profile)); - fmt.push(' '); - } - - format!("{} = {} {} {} {}", - profile.markup(&self.inner3.name, ColorClass::Var), - profile.markup("call", ColorClass::Instr), - profile.markup(&self.inner1.ty.ret.to_string(), ColorClass::Ty), - profile.markup(&self.inner1.name, ColorClass::Name), - fmt - ) - } - - fn as_any(&self) -> &dyn Any { - self - } - - fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { - if self.inner3.ty != self.inner1.ty.ret { - Err(VerifyError::Op0Op1TyNoMatch(self.inner3.ty, self.inner1.ty.ret))? - } - - let mut index = 0; - let args = &self.inner1.ty.args; - for arg in &self.inner2 { - if index < args.len() { - if matches!(args.get(index), Some((_, argty)) if *argty != (*arg).ty.into()) { - Err(VerifyError::InvalidArgumentTypeFound)? - } - } else { - if !self.inner1.ty.any_args { - Err(VerifyError::ToManyArgumentsWereSupplyed)? - } - } - - index += 1; - } - - Ok(()) - } - - fn clone_box(&self) -> Box { - Box::from( self.clone() ) - } - - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { - registry.getCompileFuncForCall()(self, registry) - } - - fn uses(&self, var: &Var) -> bool { - let mut uses = false; - - if self.inner3 == *var { - uses = true; - } - - - for arg in &self.inner2 { - if *arg == *var { - uses = true; - } - } - - uses - } -} - -/// Trait for the return instruction -/// Used for overloading the BuildRet function -pub trait BuildReturn { - /// Returns specified value - fn BuildRet(&mut self, val: T); -} - -impl BuildReturn for IRBuilder<'_> { - fn BuildRet(&mut self, val: Type) { - self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one") - .push_ir(Return::new(val)) - } -} - -impl BuildReturn for IRBuilder<'_> { - fn BuildRet(&mut self, var: Var) { - self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one") - .push_ir(Return::new(var)) - } -} - -/// Trait for the cast instruction -/// Used for overloading the BuildCast function -pub trait BuildCast { - /// builds an cast to form one variable into another type - fn BuildCast(&mut self, var: T, ty: U) -> Var; -} - -impl BuildCast for IRBuilder<'_> { - fn BuildCast(&mut self, var: Var, ty: TypeMetadata) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let out = Var::new(block, ty); - - block.push_ir(Cast::new(var, ty, out.clone())); - - out - } -} - -/// Trait for the call instruction -/// Used for overloading the BuildCall function -pub trait BuildCall { - /// builds a function call - fn BuildCall(&mut self, func: T, args: U) -> Var; -} -impl BuildCall<&Function, Vec> for IRBuilder<'_> { - fn BuildCall(&mut self, func: &Function, args: Vec) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let out = Var::new(block, func.ty.ret); - - block.push_ir(Call::new(func.clone(), args, out.clone())); - - out - } -} - -/// Trait used for overloading the BuildAssign function -pub trait BuildAssign { - /// builds an assignment - fn BuildAssign(&mut self, value: T) -> Var; -} -impl BuildAssign for IRBuilder<'_> { - fn BuildAssign(&mut self, value: Type) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let out = Var::new(block, value.into()); - - block.push_ir(ConstAssign::new(out.clone(), value)); - - out - } -} - -impl BuildAssign for IRBuilder<'_> { - fn BuildAssign(&mut self, value: Var) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let out = Var::new(block, value.ty); - - block.push_ir(ConstAssign::new(out.clone(), value)); - - out - } -} - -impl BuildAssign<&Const> for IRBuilder<'_> { - fn BuildAssign(&mut self, value: &Const) -> Var { - let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); - - let out = Var::new(block, TypeMetadata::ptr); - - block.push_ir(ConstAssign::new(out.clone(), value.clone())); - - out - } -} - -/// The ir trait -pub(crate) trait Ir: Debug + Any { - /// Returns the ir node as his textual representation - fn dump(&self) -> String; - /// Returns the ir node as his textual representation with colors - fn dumpColored(&self, profile: ColorProfile) -> String; - - /// Turns the ir node to an any - fn as_any(&self) -> &dyn Any; - - fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError>; - - /// Clones the node into a box of `Box` - fn clone_box(&self) -> Box; - - /// Compiles the node based on the given target - fn compile(&self, registry: &mut TargetBackendDescr) -> Vec; - - /// Returns if the node uses the variable - fn uses(&self, _: &Var) -> bool { - false - } - - fn is(&self, other: &Box) -> bool { - other.dump() == self.dump() - } -} - -impl PartialEq for Box { - fn eq(&self, other: &Self) -> bool { - self.is(other) - } -} - -impl Eq for Box { } - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_box() - } - - fn clone_from(&mut self, source: &Self) { - *self = source.clone() - } -} - -/// Used for sus workaround to replace current ir node -pub trait Replace { - /// Replaces current ir node - fn replace(&mut self, other: T); -} - -impl Replace> for Box { - fn replace(&mut self, other: Box) { - *self = other - } -} \ No newline at end of file diff --git a/src/IR/mod.rs b/src/IR/mod.rs index a0d5a19e..0a259cdb 100644 --- a/src/IR/mod.rs +++ b/src/IR/mod.rs @@ -5,8 +5,11 @@ mod builder; mod block; mod var; mod constant; +mod nodes; /// Stores all ir nodes and the ir trait -pub mod ir; +pub mod ir { + pub use super::nodes::*; +} use std::error::Error; use std::fmt::Display; diff --git a/src/IR/nodes/assign.rs b/src/IR/nodes/assign.rs new file mode 100644 index 00000000..8eb40d04 --- /dev/null +++ b/src/IR/nodes/assign.rs @@ -0,0 +1,164 @@ +use super::*; + +impl Ir for ConstAssign { + fn dump(&self) -> String { + let meta: TypeMetadata = self.inner2.into(); + format!("{} = {} {}", self.inner1.name, meta, self.inner2.val()) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + let meta: TypeMetadata = self.inner2.into(); + format!("{} = {} {}", + profile.markup(&self.inner1.name, ColorClass::Var), + profile.markup(&meta.to_string(), ColorClass::Instr), + profile.markup(&self.inner2.val().to_string(), ColorClass::Value), + ) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + let op0Ty = self.inner1.ty; + let op1Ty = self.inner2.into(); + if op0Ty != op1Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? + } + + Ok(()) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForConstAssign()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 { true } + else { false } + } +} + +impl Ir for ConstAssign { + fn dump(&self) -> String { + let meta: TypeMetadata = self.inner2.ty; + format!("{} = {} {}", self.inner1.name, meta, self.inner2.name) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + let meta: TypeMetadata = self.inner2.ty; + format!("{} = {} {}", + profile.markup(&self.inner1.name, ColorClass::Var), + profile.markup(&meta.to_string(), ColorClass::Instr), + profile.markup(&self.inner2.name.to_string(), ColorClass::Value), + ) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + let op0Ty = self.inner1.ty; + let op1Ty = self.inner2.ty; + if op0Ty != op1Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? + } + + Ok(()) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForConstAssignVar()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 { true } + else if *var == self.inner2 { true } + else { false } + } +} + +impl Ir for ConstAssign { + fn dump(&self) -> String { + format!("{} = ptr {}", self.inner1.name, self.inner2.name) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} = {} {}", + profile.markup(&self.inner1.name, ColorClass::Var), + profile.markup("ptr", ColorClass::Ty), + profile.markup(&self.inner2.name.to_string(), ColorClass::Value), + ) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + Ok(()) + } + + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForConstAssignConst()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 { true } + else { false } + } +} + +/// Trait used for overloading the BuildAssign function +pub trait BuildAssign { + /// builds an assignment + fn BuildAssign(&mut self, value: T) -> Var; +} +impl BuildAssign for IRBuilder<'_> { + fn BuildAssign(&mut self, value: Type) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let out = Var::new(block, value.into()); + + block.push_ir(ConstAssign::new(out.clone(), value)); + + out + } +} + +impl BuildAssign for IRBuilder<'_> { + fn BuildAssign(&mut self, value: Var) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let out = Var::new(block, value.ty); + + block.push_ir(ConstAssign::new(out.clone(), value)); + + out + } +} + +impl BuildAssign<&Const> for IRBuilder<'_> { + fn BuildAssign(&mut self, value: &Const) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let out = Var::new(block, TypeMetadata::ptr); + + block.push_ir(ConstAssign::new(out.clone(), value.clone())); + + out + } +} diff --git a/src/IR/nodes/call.rs b/src/IR/nodes/call.rs new file mode 100644 index 00000000..07be57f2 --- /dev/null +++ b/src/IR/nodes/call.rs @@ -0,0 +1,103 @@ +use super::*; + +impl Ir for Call, Var> { + fn dump(&self) -> String { + let mut fmt = String::new(); + + for arg in &self.inner2 { + fmt.push_str(&format!("{} ", arg)) + } + + format!("{} = call {} {} {}", self.inner3.name, self.inner1.ty.ret, self.inner1.name, fmt) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + let mut fmt = String::new(); + + for arg in &self.inner2 { + fmt.push_str(&arg.to_colored_string(profile)); + fmt.push(' '); + } + + format!("{} = {} {} {} {}", + profile.markup(&self.inner3.name, ColorClass::Var), + profile.markup("call", ColorClass::Instr), + profile.markup(&self.inner1.ty.ret.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.name, ColorClass::Name), + fmt + ) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + if self.inner3.ty != self.inner1.ty.ret { + Err(VerifyError::Op0Op1TyNoMatch(self.inner3.ty, self.inner1.ty.ret))? + } + + let mut index = 0; + let args = &self.inner1.ty.args; + for arg in &self.inner2 { + if index < args.len() { + if matches!(args.get(index), Some((_, argty)) if *argty != (*arg).ty.into()) { + Err(VerifyError::InvalidArgumentTypeFound)? + } + } else { + if !self.inner1.ty.any_args { + Err(VerifyError::ToManyArgumentsWereSupplyed)? + } + } + + index += 1; + } + + Ok(()) + } + + fn clone_box(&self) -> Box { + Box::from( self.clone() ) + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForCall()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + let mut uses = false; + + if self.inner3 == *var { + uses = true; + } + + + for arg in &self.inner2 { + if *arg == *var { + uses = true; + } + } + + uses + } +} + + + +/// Trait for the call instruction +/// Used for overloading the BuildCall function +pub trait BuildCall { + /// builds a function call + fn BuildCall(&mut self, func: T, args: U) -> Var; +} +impl BuildCall<&Function, Vec> for IRBuilder<'_> { + fn BuildCall(&mut self, func: &Function, args: Vec) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let out = Var::new(block, func.ty.ret); + + block.push_ir(Call::new(func.clone(), args, out.clone())); + + out + } +} diff --git a/src/IR/nodes/cast.rs b/src/IR/nodes/cast.rs new file mode 100644 index 00000000..ac7e2097 --- /dev/null +++ b/src/IR/nodes/cast.rs @@ -0,0 +1,63 @@ +use super::*; + +impl Ir for Cast { + fn dump(&self) -> String { + format!("{} = cast {} to {}", self.inner3.name, self.inner1.name, self.inner2) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} = {} {} {} {}", + profile.markup(&self.inner3.name, ColorClass::Var), + profile.markup(&"cast", ColorClass::Instr), + profile.markup(&self.inner1.name, ColorClass::Var), + profile.markup(&"to", ColorClass::Instr), + profile.markup(&self.inner2.to_string(), ColorClass::Ty), + ) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + if self.inner3.ty != self.inner2 { + Err(VerifyError::Op0Op1TyNoMatch(self.inner3.ty, self.inner2))? + } + Ok(()) + } + + fn uses(&self, var: &Var) -> bool { + let var = var.to_owned(); + + if var == self.inner1 || var == self.inner3 { + true + } else { false } + } + + fn clone_box(&self) -> Box { + Box::from( self.clone() ) + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForCastTyVar()(self, registry) + } +} + +/// Trait for the cast instruction +/// Used for overloading the BuildCast function +pub trait BuildCast { + /// builds an cast to form one variable into another type + fn BuildCast(&mut self, var: T, ty: U) -> Var; +} + +impl BuildCast for IRBuilder<'_> { + fn BuildCast(&mut self, var: Var, ty: TypeMetadata) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let out = Var::new(block, ty); + + block.push_ir(Cast::new(var, ty, out.clone())); + + out + } +} diff --git a/src/IR/nodes/math.rs b/src/IR/nodes/math.rs new file mode 100644 index 00000000..6e0fd94f --- /dev/null +++ b/src/IR/nodes/math.rs @@ -0,0 +1,217 @@ +use super::*; + +macro_rules! MathIrNode { + ($name:ident, $compileFuncVarVar:ident, $compileFuncVarTy:ident, $compileFuncTyTy:ident, $buildTraitName:ident, $buildFuncName:ident, $dump:expr) => { + /// Used for overloading the build function + pub trait $buildTraitName { + /// Xors values + fn $buildFuncName(&mut self, op0: T, op1: U) -> Var; + } + + impl $buildTraitName for IRBuilder<'_> { + fn $buildFuncName(&mut self, op0: Type, op1: Type) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let op0Ty: TypeMetadata = op0.into(); + + let ty = op0Ty; // now both types need to be the same + let var = Var::new(block, ty); + + block.push_ir($name::new(op0, op1, var.clone())); + + var + } + } + + impl $buildTraitName for IRBuilder<'_> { + fn $buildFuncName(&mut self, op0: Var, op1: Var) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let op0Ty: TypeMetadata = op0.ty.into(); + + let ty = op0Ty; + let var = Var::new(block, ty); + + block.push_ir($name::new(op0, op1, var.clone())); + + var + } + } + + impl $buildTraitName for IRBuilder<'_> { + fn $buildFuncName(&mut self, op0: Var, op1: Type) -> Var { + let block = self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one"); + + let op0Ty: TypeMetadata = op0.ty.into(); + + let ty = op0Ty; + let var = Var::new(block, ty); + + block.push_ir($name::new(op0, op1, var.clone())); + + var + } + } + + impl Ir for $name { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn dump(&self) -> String { + format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner3.ty, self.inner1.val(), self.inner2.val()) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} = {} {} {}, {}", + profile.markup(&self.inner3.name, ColorClass::Var), + profile.markup($dump, ColorClass::Instr), + profile.markup(&self.inner3.ty.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.val().to_string(), ColorClass::Value), + profile.markup(&self.inner2.val().to_string(), ColorClass::Value) + ) + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + let op0Ty: TypeMetadata = self.inner1.into(); + let op1Ty: TypeMetadata = self.inner2.into(); + let op2Ty: TypeMetadata = self.inner3.ty.into(); + + if !(op0Ty == op1Ty && op1Ty == op2Ty) { + if op0Ty != op1Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? + } else if op1Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? + } if op0Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? + } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } + } + + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.$compileFuncTyTy()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner3 { true } + else { false } + } + } + + impl Ir for $name { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn dump(&self) -> String { + format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner3.ty, self.inner1.name, self.inner2.name) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} = {} {} {}, {}", + profile.markup(&self.inner3.name, ColorClass::Var), + profile.markup($dump, ColorClass::Instr), + profile.markup(&self.inner3.ty.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.name.to_string(), ColorClass::Var), + profile.markup(&self.inner2.name.to_string(), ColorClass::Var) + ) + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + let op0Ty: TypeMetadata = self.inner1.ty.into(); + let op1Ty: TypeMetadata = self.inner2.ty.into(); + let op2Ty: TypeMetadata = self.inner3.ty.into(); + + if !(op0Ty == op1Ty && op1Ty == op2Ty) { + if op0Ty != op1Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? + } else if op1Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? + } if op0Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? + } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } + } + + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.$compileFuncVarVar()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 || *var == self.inner2 || *var == self.inner3 { true } + else { false } + } + } + + impl Ir for $name { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn dump(&self) -> String { + format!("{} = {} {} {}, {}", $dump, self.inner3.name, self.inner1.ty, self.inner1.name, self.inner2.val()) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} = {} {} {}, {}", + profile.markup(&self.inner3.name, ColorClass::Var), + profile.markup($dump, ColorClass::Instr), + profile.markup(&self.inner1.ty.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.name.to_string(), ColorClass::Var), + profile.markup(&self.inner2.val().to_string(), ColorClass::Var) + ) + } + + fn verify(&self, _: FunctionType) -> Result<(), VerifyError> { + let op0Ty: TypeMetadata = self.inner1.ty.into(); + let op1Ty: TypeMetadata = self.inner3.ty.into(); + let op2Ty: TypeMetadata = self.inner2.into(); + + if !(op0Ty == op1Ty && op1Ty == op2Ty) { + if op0Ty != op1Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op1Ty))? + } else if op1Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op1Ty, op2Ty))? + } if op0Ty != op2Ty { + Err(VerifyError::Op0Op1TyNoMatch(op0Ty, op2Ty))? + } else { todo!("unknown error variant (debug: ty0 {} ty1 {} ty2 {})", op0Ty, op1Ty, op2Ty) } + } + + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.$compileFuncVarTy()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 || *var == self.inner3 { true } + else { false } + } + } + + }; +} + +MathIrNode!(Add, getCompileFuncForAddVarVar, getCompileFuncForAddVarType, getCompileFuncForAddTypeType, BuildAdd, BuildAdd, "add"); +MathIrNode!(Sub, getCompileFuncForSubVarVar, getCompileFuncForSubVarType, getCompileFuncForSubTypeType, BuildSub, BuildSub, "sub"); +MathIrNode!(Xor, getCompileFuncForXorVarVar, getCompileFuncForXorVarType, getCompileFuncForXorTypeType, BuildXor, BuildXor, "xor"); +MathIrNode!(Or, getCompileFuncForOrVarVar, getCompileFuncForOrVarType, getCompileFuncForOrTypeType, BuildOr, BuildOr, "or"); +MathIrNode!(And, getCompileFuncForAndVarVar, getCompileFuncForAndVarType, getCompileFuncForAndTypeType, BuildAnd, BuildAnd, "and"); + diff --git a/src/IR/nodes/mod.rs b/src/IR/nodes/mod.rs new file mode 100644 index 00000000..1c3bd513 --- /dev/null +++ b/src/IR/nodes/mod.rs @@ -0,0 +1,165 @@ +use std::{any::Any, fmt::Debug, hash::Hash}; +use super::{Const, Function, FunctionType, IRBuilder, Type, TypeMetadata, Var, VerifyError}; +use crate::Target::{instr::Instr, TargetBackendDescr}; + +mod assign; +mod call; +mod cast; +mod math; +mod ret; + +pub use assign::*; +pub use call::*; +pub use cast::*; +pub use math::*; +pub use ret::*; + +macro_rules! IrTypeWith3 { + ($name:tt, $param1:tt, $param2:tt, $param3:tt) => { + /// An Ir node + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $name<$param1, $param2, $param3> { + /// first inner value + pub(crate) inner1: $param1, + /// second inner value + pub(crate) inner2: $param2, + /// third inner value + pub(crate) inner3: $param3, + } + + + impl<$param1, $param2, $param3> $name<$param1, $param2, $param3> { + /// Creates new instance + #[allow(dead_code)] + pub fn new(op0: $param1, op1: $param2, op2: $param3) -> Box { + Box::from( + Self { + inner1: op0, + inner2: op1, + inner3: op2, + } + ) + } + } + }; +} +macro_rules! IrTypeWith2 { + ($name:tt, $param1:tt, $param2:tt) => { + /// An Ir node + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $name<$param1, $param2> { + /// first inner value + pub inner1: $param1, + /// second inner value + pub inner2: $param2, + } + + + impl<$param1, $param2> $name<$param1, $param2> { + /// Creates new instance + #[allow(dead_code)] + pub fn new(op0: $param1, op1: $param2) -> Box { + Box::from( + Self { + inner1: op0, + inner2: op1, + } + ) + } + } + }; +} +macro_rules! IrTypeWith1 { + ($name:tt, $param1:tt) => { + /// An Ir node + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub(crate) struct $name<$param1> { + /// inner value + pub(crate) inner1: $param1, + } + + + impl<$param1> $name<$param1> { + /// Creates new instance + #[allow(dead_code)] + pub(crate) fn new(op0: $param1) -> Box { + Box::from( + Self { + inner1: op0, + } + ) + } + } + }; +} + +IrTypeWith1!(Return, T); +IrTypeWith3!(Call, T, U, Z); +IrTypeWith2!(ConstAssign, T, U); +IrTypeWith3!(Cast, T, U, Z); +IrTypeWith3!(Add, T, U, Z); +IrTypeWith3!(Sub, T, U, Z); +IrTypeWith3!(Xor, T, U, Z); +IrTypeWith3!(Or, T, U, Z); +IrTypeWith3!(And, T, U, Z); + +use crate::Support::{ColorClass, ColorProfile}; + + +/// The ir trait +pub(crate) trait Ir: Debug + Any { + /// Returns the ir node as his textual representation + fn dump(&self) -> String; + /// Returns the ir node as his textual representation with colors + fn dumpColored(&self, profile: ColorProfile) -> String; + + /// Turns the ir node to an any + fn as_any(&self) -> &dyn Any; + + fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError>; + + /// Clones the node into a box of `Box` + fn clone_box(&self) -> Box; + + /// Compiles the node based on the given target + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec; + + /// Returns if the node uses the variable + fn uses(&self, _: &Var) -> bool { + false + } + + fn is(&self, other: &Box) -> bool { + other.dump() == self.dump() + } +} + +impl PartialEq for Box { + fn eq(&self, other: &Self) -> bool { + self.is(other) + } +} + +impl Eq for Box { } + +impl Clone for Box { + fn clone(&self) -> Box { + self.clone_box() + } + + fn clone_from(&mut self, source: &Self) { + *self = source.clone() + } +} + +/// Used for sus workaround to replace current ir node +pub trait Replace { + /// Replaces current ir node + fn replace(&mut self, other: T); +} + +impl Replace> for Box { + fn replace(&mut self, other: Box) { + *self = other + } +} \ No newline at end of file diff --git a/src/IR/nodes/ret.rs b/src/IR/nodes/ret.rs new file mode 100644 index 00000000..b33a03af --- /dev/null +++ b/src/IR/nodes/ret.rs @@ -0,0 +1,101 @@ +use super::*; + +impl Ir for Return { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn dump(&self) -> String { + let metadata: TypeMetadata = self.inner1.into(); + format!("ret {} {}", metadata, self.inner1.val()) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + let metadata: TypeMetadata = self.inner1.into(); + format!("{} {} {}", + profile.markup("ret", ColorClass::Instr), + profile.markup(&metadata.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.val().to_string(), ColorClass::Var), + ) + } + + fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError> { + let ty: TypeMetadata = self.inner1.into(); + + if ty != FuncTy.ret { + Err(VerifyError::RetTyNotFnTy(ty, FuncTy.ret))? + } + + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForRetType()(self, registry) + } +} + +impl Ir for Return { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } + + fn dump(&self) -> String { + format!("ret {} {}", self.inner1.ty, self.inner1.name) + } + + fn dumpColored(&self, profile: ColorProfile) -> String { + format!("{} {} {}", + profile.markup("ret", ColorClass::Instr), + profile.markup(&self.inner1.ty.to_string(), ColorClass::Ty), + profile.markup(&self.inner1.name.to_string(), ColorClass::Var), + ) + } + + fn verify(&self, FuncTy: FunctionType) -> Result<(), VerifyError> { + let ty: TypeMetadata = self.inner1.ty.into(); + + if ty != FuncTy.ret { + Err(VerifyError::RetTyNotFnTy(ty, FuncTy.ret))? + } + + Ok(()) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn compile(&self, registry: &mut TargetBackendDescr) -> Vec { + registry.getCompileFuncForRetVar()(self, registry) + } + + fn uses(&self, var: &Var) -> bool { + if *var == self.inner1 { true } + else { false } + } +} + +/// Trait for the return instruction +/// Used for overloading the BuildRet function +pub trait BuildReturn { + /// Returns specified value + fn BuildRet(&mut self, val: T); +} + +impl BuildReturn for IRBuilder<'_> { + fn BuildRet(&mut self, val: Type) { + self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one") + .push_ir(Return::new(val)) + } +} + +impl BuildReturn for IRBuilder<'_> { + fn BuildRet(&mut self, var: Var) { + self.blocks.get_mut(self.curr).expect("the IRBuilder needs to have an current block\nConsider creating one") + .push_ir(Return::new(var)) + } +} \ No newline at end of file diff --git a/src/Target/x64/compilation/assign.rs b/src/Target/x64/compilation/assign.rs new file mode 100644 index 00000000..dbd66ebf --- /dev/null +++ b/src/Target/x64/compilation/assign.rs @@ -0,0 +1,152 @@ +use super::*; + +pub(crate) fn CompileConstAssign(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { + let infos = &mut registry.backend; + let block = registry.block.unwrap(); + + let ty = &assign.inner1.ty; + + let boxed: Box = Box::new(assign.clone()); + + if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + let store = { + if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { + VarStorage::Register(reg) + } else { + let addend = match ty { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => todo!("cant output an assing somthing to void"), + }; + + infos.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) + } + }; + + infos.insertVar( + assign.inner1.clone(), + store.clone() + ); + + if let VarStorage::Register(reg) = &store { + vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Imm(assign.inner2.val() as i64)) ] + } else if let VarStorage::Memory(mem) = &store { + vec![ Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Imm(assign.inner2.val() as i64)) ] + } else { todo!() } +} + +pub(crate) fn CompileConstAssignVar(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { + let infos = &mut registry.backend; + let block = registry.block.unwrap(); + + let loc = if let Some(loc) = infos.varsStorage.get_key_value(&assign.inner2) { + loc.1.clone() + } else { + panic!("unknown variable: {:?}", assign.inner2) + }; + + let ty = &assign.inner1.ty; + + let boxed: Box = Box::new(assign.clone()); + + if !block.isVarUsedAfterNode(&boxed, &assign.inner2) { + infos.drop(&assign.inner2); + } + if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + let store = { + if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { + VarStorage::Register(reg) + } else { + let addend = match ty { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => todo!("cant output an assing somthing to void"), + }; + + infos.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) + } + }; + + infos.insertVar( + assign.inner1.clone(), + store.clone() + ); + + if let VarStorage::Register(reg) = &store { + if let VarStorage::Register(reg2) = &loc { + vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(reg2.boxed())) ] + } else if let VarStorage::Memory(mem2) = &loc { + vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Mem(mem2.clone())) ] + } else { unreachable!() } + } else if let VarStorage::Memory(mem) = &store { + if let VarStorage::Register(reg2) = &loc { + vec![ Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(reg2.boxed())) ] + } else if let VarStorage::Memory(mem2) = &loc { + vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(mem2.clone())), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem2.clone()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) + ] + } else { unreachable!() } + } else { todo!() } +} + +pub(crate) fn CompileConstAssignConst(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { + let block = registry.block.unwrap(); + + registry.backend.stackSafe = true; + + let infos = &mut registry.backend; + + let ty = &assign.inner1.ty; + + let boxed: Box = Box::new(assign.clone()); + + if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + let store = { + if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { + VarStorage::Register(reg) + } else { + let addend = match ty { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => todo!("cant output an assing somthing to void"), + }; + + infos.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) + } + }; + + infos.insertVar( + assign.inner1.clone(), + store.clone() + ); + + if let VarStorage::Register(reg) = &store { + vec![ + Instr::with2(Mnemonic::Lea, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(MemOp { base: None, index: None, scale: 0, displ: 1, rip: true })), + Instr::with1(Mnemonic::Link, Operand::LinkDestination(assign.inner2.name.to_string(), -4)), + Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) + ] + } else if let VarStorage::Memory(mem) = &store { + vec![ + Instr::with2(Mnemonic::Lea, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(MemOp { base: None, index: None, scale: 0, displ: 1, rip: true })), + Instr::with1(Mnemonic::Link, Operand::LinkDestination(assign.inner2.name.to_string(), -4)), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) + ] + } else { todo!() } +} diff --git a/src/Target/x64/compilation/call.rs b/src/Target/x64/compilation/call.rs new file mode 100644 index 00000000..f586f371 --- /dev/null +++ b/src/Target/x64/compilation/call.rs @@ -0,0 +1,120 @@ +use super::*; + +pub(crate) fn CompileCall(call: &Call, Var>, registry: &mut TargetBackendDescr) -> Vec { + let boxed: Box = Box::new(call.clone()); + let block = registry.block.unwrap(); + + let mut asm = vec![]; + + for reg in vec![x64Reg::Rcx, x64Reg::Rdx, x64Reg::Rsi, x64Reg::Rdi, x64Reg::Rsi] { // save mutable registers + if !registry.backend.openUsableRegisters64.contains(®.boxed()) { + let var = registry.backend.getVarByReg(reg.boxed()).cloned(); + + if let Some(var) = var { + if block.isVarUsedAfterNode(&boxed, &var) { + asm.push(Instr::with1(Mnemonic::Push, Operand::Reg(reg.boxed()))); + } + } + } + } + + let func = &call.inner1; + + let mut reg_args = 0; + + for arg in &call.inner2 { + let loc = registry.backend.varsStorage.get_key_value(arg).expect("expected valid variable as arg input"); + + match loc.1 { + VarStorage::Register(reg) => { + if reg_args < registry.call.regArgs() { + match arg.ty { + TypeMetadata::i64 | TypeMetadata::u64 | TypeMetadata::ptr => + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args64()[reg_args].boxed()), Operand::Reg(reg.clone()))), + TypeMetadata::i32 | TypeMetadata::u32 => + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args32()[reg_args].boxed()), Operand::Reg(reg.clone()))), + TypeMetadata::i16 | TypeMetadata::u16 => + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args16()[reg_args].boxed()), Operand::Reg(reg.clone()))), + TypeMetadata::Void => {}, + } + + reg_args += 1; + } else { + asm.push( Instr::with1(Mnemonic::Push, Operand::Reg(reg.clone()))); + } + }, + VarStorage::Memory(mem) => { + if reg_args < registry.call.regArgs() { + if arg.ty == TypeMetadata::i64 || arg.ty == TypeMetadata::u64 { + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args64()[reg_args].boxed()), Operand::Mem(mem.clone()))); + } else if arg.ty == TypeMetadata::i32 || arg.ty == TypeMetadata::u32 { + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args32()[reg_args].boxed()), Operand::Mem(mem.clone()))); + } else if arg.ty == TypeMetadata::i16 || arg.ty == TypeMetadata::u16 { + asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args16()[reg_args].boxed()), Operand::Mem(mem.clone()))); + } + reg_args += 1; + } else { + asm.push( Instr::with1(Mnemonic::Push, Operand::Mem(mem.clone()))); + } + }, + } + if !block.isVarUsedAfterNode(&boxed, arg) { + registry.backend.drop(arg); + } + } + + if registry.call.reset_eax() { + asm.push(Instr::with2(Mnemonic::Xor, Operand::Reg(x64Reg::Eax.boxed()), Operand::Reg(x64Reg::Eax.boxed()))); + } + + asm.push( Instr::with1(Mnemonic::Call, Operand::Imm(0))); + + asm.push( Instr::with1(Mnemonic::Link, Operand::LinkDestination(call.inner1.name.to_string(), -4))); + + if func.ty.ret != TypeMetadata::Void { + let store = if let Some(reg) = registry.backend.getOpenRegBasedOnTy(call.inner3.ty) { + match func.ty.ret { + TypeMetadata::u16 | TypeMetadata::i16 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret16().boxed())) ), + TypeMetadata::u32 | TypeMetadata::i32 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret32().boxed())) ), + TypeMetadata::u64 | TypeMetadata::i64 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret64().boxed())) ), + _ => unreachable!(), + }; + VarStorage::Register(reg) + } else { + let addend = match call.inner3.ty { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => unreachable!(), + }; + + registry.backend.currStackOffsetForLocalVars += addend; + let mem = x64Reg::Rbp - (registry.backend.currStackOffsetForLocalVars - addend) as u32; + + match func.ty.ret { + TypeMetadata::u16 | TypeMetadata::i16 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret16().boxed())) ), + TypeMetadata::u32 | TypeMetadata::i32 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret32().boxed())) ), + TypeMetadata::u64 | TypeMetadata::i64 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret64().boxed())) ), + _ => unreachable!(), + }; + + VarStorage::Memory(mem) + }; + + registry.backend.insertVar(call.inner3.clone(), store); + } + + for reg in vec![x64Reg::Rcx, x64Reg::Rdx, x64Reg::Rsi, x64Reg::Rdi, x64Reg::Rsi] { // getback mutable registers + if !registry.backend.openUsableRegisters64.contains(®.boxed()) { + let var = registry.backend.getVarByReg(reg.boxed()).cloned(); + + if let Some(var) = var { + if block.isVarUsedAfterNode(&boxed, &var) { + asm.push(Instr::with1(Mnemonic::Pop, Operand::Reg(reg.boxed()))); + } + } + } + } + + asm +} diff --git a/src/Target/x64/compilation/cast.rs b/src/Target/x64/compilation/cast.rs new file mode 100644 index 00000000..1a3b9fd9 --- /dev/null +++ b/src/Target/x64/compilation/cast.rs @@ -0,0 +1,74 @@ +use super::*; + +pub(crate) fn CompileCast(cast: &Cast, registry: &mut TargetBackendDescr) -> Vec { + let boxed: Box = Box::new(cast.clone()); + let block = registry.block.unwrap(); + + let loc = if let Some(loc) = registry.backend.varsStorage.get(&cast.inner1) { + loc.clone() + } else { + panic!("unknown variable: {:?}", cast.inner1) + }; + + if block.isVarUsedAfterNode(&boxed, &cast.inner1) { + registry.backend.drop(&cast.inner1); + } + if block.isVarUsedAfterNode(&boxed, &cast.inner3) { + return vec![]; + } + + let store = { + if let Some(reg) = registry.backend.getOpenRegBasedOnTy(cast.inner2) { + VarStorage::Register(reg) + } else { + let addend = match cast.inner2 { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 => 8, + TypeMetadata::Void => todo!("cant cast into void"), + TypeMetadata::ptr => todo!("cant cast into ptr"), + }; + + registry.backend.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (registry.backend.currStackOffsetForLocalVars - addend) as u32) + } + }; + + registry.backend.insertVar( + cast.inner3.clone(), + store.clone() + ); + + match loc { + VarStorage::Register(inbound) => { + if let VarStorage::Register(outboud) = store { + if cast.inner1.ty.bitSize() > cast.inner3.ty.bitSize() { + if inbound.is_gr16() || inbound.is_gr8() { // zero extend + return vec![ + Instr::with2(Mnemonic::Movzx, Operand::Reg(outboud), Operand::Reg(inbound)), + ]; + } else { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(outboud), Operand::Reg(inbound)) + ]; + } + } else { + return vec![{ + if inbound.is_gr64() { + Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub64()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub64()).unwrap().boxed())) + } else if inbound.is_gr32() { + Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub32()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub32()).unwrap().boxed())) + } else if inbound.is_gr16() { + Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub16()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub16()).unwrap().boxed())) + } else if inbound.is_gr8() { + Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub8()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub8()).unwrap().boxed())) + } else { panic!() } + }]; + } + } + }, + VarStorage::Memory(_) => todo!(), + } + + vec![] +} diff --git a/src/Target/x64/compilation/math.rs b/src/Target/x64/compilation/math.rs new file mode 100644 index 00000000..a5268797 --- /dev/null +++ b/src/Target/x64/compilation/math.rs @@ -0,0 +1,225 @@ +use super::*; + +macro_rules! CompileMathVarVar { + ($name:ident, $node:ident, $mnemonic:expr) => { + pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { + let infos = &mut registry.backend; + let block = registry.block.unwrap(); + + let loc1 = if let Some(loc1) = infos.varsStorage.get(&node.inner1) { + loc1.clone() + } else { + panic!("unknown variable: {:?}", node.inner1) + }; + + let loc2 = if let Some(loc2) = infos.varsStorage.get(&node.inner2) { + loc2.clone() + + } else { + panic!("unknown variable: {:?}", node.inner1) + }; + + let boxed: Box = Box::new(node.clone()); + + if !block.isVarUsedAfterNode(&boxed, &node.inner1) { + infos.drop(&node.inner1); + } + if !block.isVarUsedAfterNode(&boxed, &node.inner2) { + infos.drop(&node.inner2); + } + if !block.isVarUsedAfterNode(&boxed, &node.inner3) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + let ty = &node.inner1.ty; + + let ret = { + if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { + VarStorage::Register(reg) + } else { + let addend = match ty { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => todo!("cant output an addition into an void"), + }; + + infos.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) + } + }; + + infos.insertVar( + node.inner3.clone(), + ret.clone() + ); + let tmp = infos.getTmpBasedOnTy(*ty); + + if let VarStorage::Register(loc1Reg) = &loc1 { + if let VarStorage::Register(loc2Reg) = &loc2 { + if let VarStorage::Register(reg) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc2Reg.boxed())), + Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), + ] + } else if let VarStorage::Memory(mem) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc2Reg.boxed())), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), + ]; + } else { todo!() } + } + } + + if let VarStorage::Memory(mem1) = loc1 { + if let VarStorage::Memory(mem2) = loc2 { + if let VarStorage::Register(reg) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Mem(mem2.clone())), + Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), + ]; + } else if let VarStorage::Memory(mem) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Mem(mem2.clone())), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), + ]; + } else { todo!() } + } + } + + todo!(); // nothing was compiled + } + }; +} + +macro_rules! CompileMathVarType { + ($name:ident, $node:ident, $mnemonic:expr) => { + pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { + let infos = &mut registry.backend; + let block = registry.block.unwrap(); + + let loc1 = if let Some(loc1) = infos.varsStorage.get(&node.inner1) { + loc1.clone() + } else { + panic!("unknown variable: {:?}", node.inner1) + }; + + let boxed: Box = Box::new(node.clone()); + + if !block.isVarUsedAfterNode(&boxed, &node.inner1) { + infos.drop(&node.inner1); + } + if !block.isVarUsedAfterNode(&boxed, &node.inner3) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + let ty = &node.inner1.ty; + + let ret = { + if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { + VarStorage::Register(reg) + } else { + let addend = match ty { + TypeMetadata::u16 | TypeMetadata::i16=> 2, + TypeMetadata::u32 | TypeMetadata::i32=> 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => todo!("cant output an addition into an void"), + }; + + infos.currStackOffsetForLocalVars += addend; + VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) + } + }; + + infos.insertVar( + node.inner3.clone(), + ret.clone() + ); + let tmp = infos.getTmpBasedOnTy(*ty); + + if let VarStorage::Register(loc1Reg) = &loc1 { + if let VarStorage::Register(reg) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), + Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), + ] + } else if let VarStorage::Memory(mem) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), + ]; + } else { todo!() } + } + + if let VarStorage::Memory(mem1) = loc1 { + if let VarStorage::Register(reg) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), + Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), + ]; + } else if let VarStorage::Memory(mem) = &ret { + return vec![ + Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), + Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), + Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), + ]; + } else { todo!() } + } + + todo!(); // nothing was compiled + } + }; +} + +macro_rules! CompileMathTyTy { + ($name:ident, $node:ident, $op:tt) => { + pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { + let val = node.inner1.val() $op node.inner2.val(); + let block = registry.block.unwrap(); + + let boxed: Box = Box::new(node.clone()); + + if !block.isVarUsedAfterNode(&boxed, &node.inner3) { + return vec![]; // all of these calculations don't need to be done: dead code removal + } + + CompileConstAssign(&ConstAssign::new(node.inner3.clone(), { + match node.inner3.ty { + TypeMetadata::u16 => Type::u16(val as u16), + TypeMetadata::u32 => Type::u32(val as u32), + TypeMetadata::u64 => Type::u64(val as u64), + TypeMetadata::i16 => Type::i16(val as i16), + TypeMetadata::i32 => Type::i32(val as i32), + TypeMetadata::i64 => Type::i64(val as i64), + TypeMetadata::ptr => Type::ptr(val as i64), + TypeMetadata::Void =>Type::Void, + } + }), registry) + } + } +} + +CompileMathVarVar!(CompileAddVarVar, Add, Mnemonic::Add); +CompileMathVarVar!(CompileSubVarVar, Sub, Mnemonic::Sub); +CompileMathVarVar!(CompileXorVarVar, Xor, Mnemonic::Xor); +CompileMathVarVar!(CompileOrVarVar, Or, Mnemonic::Or); +CompileMathVarVar!(CompileAndVarVar, And, Mnemonic::And); + +CompileMathVarType!(CompileAddVarTy, Add, Mnemonic::Add); +CompileMathVarType!(CompileSubVarTy, Sub, Mnemonic::Sub); +CompileMathVarType!(CompileXorVarTy, Xor, Mnemonic::Xor); +CompileMathVarType!(CompileOrVarTy, Or, Mnemonic::Or); +CompileMathVarType!(CompileAndVarTy, And, Mnemonic::And); + +CompileMathTyTy!(CompileAddTyTy, Add, +); +CompileMathTyTy!(CompileSubTyTy, Sub, -); +CompileMathTyTy!(CompileXorTyTy, Xor, ^); +CompileMathTyTy!(CompileOrTyTy, Or, |); +CompileMathTyTy!(CompileAndTyTy, And, &); \ No newline at end of file diff --git a/src/Target/x64/compilation/mod.rs b/src/Target/x64/compilation/mod.rs new file mode 100644 index 00000000..67df4845 --- /dev/null +++ b/src/Target/x64/compilation/mod.rs @@ -0,0 +1,93 @@ +mod assign; +mod call; +mod cast; +mod math; +mod prolog; +mod ret; + +pub(crate) use assign::*; +pub(crate) use call::*; +pub(crate) use cast::*; +pub(crate) use math::*; +pub(crate) use prolog::*; +pub(crate) use ret::*; + + +use std::collections::VecDeque; + +use crate::prelude::{Block, Function, Type, TypeMetadata, Var}; +use crate::Target::target_descr::{TargetBackendDescr, VarStorage}; +use crate::Target::Reg; +use crate::IR::{ir::*, Const}; +use super::Optimize; + +use crate::Target::CallConv; + +use super::{x64Reg, instr::*}; + +pub(crate) fn buildAsmX86<'a>(block: &'a Block, func: &Function, call: &CallConv, registry: &mut TargetBackendDescr<'a>) -> Vec { + registry.block = Some(&block); + + let info = &mut registry.backend; + + let mut reg_vars = 0; + let mut stack_off = 8; // because in an call the return adress gets pushed which is 8 bytes long + let mut var_index = 0; + + for (_, meta) in &func.ty.args { + let mut var = Var(&mut block.clone(), meta.to_owned()); + var.name = format!("%{}", var_index); + + info.insertVar(var, { + if reg_vars >= call.regArgs() { + let addend = match meta { + TypeMetadata::u16 | TypeMetadata::i16 => 2, + TypeMetadata::u32 | TypeMetadata::i32 => 4, + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, + TypeMetadata::Void => continue, + }; + + stack_off += addend; + VarStorage::Memory(x64Reg::Rbp - (stack_off - addend)) + } else { + reg_vars += 1; + VarStorage::Register( match meta { + TypeMetadata::u16 | TypeMetadata::i16 => call.args16()[reg_vars - 1].boxed(), + TypeMetadata::u32 | TypeMetadata::i32 => call.args32()[reg_vars - 1].boxed(), + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => call.args64()[reg_vars - 1].boxed(), + TypeMetadata::Void => continue, + }) + } + }); + + var_index += 1; + } + + if reg_vars < call.regArgs() { + info.dropReg(call.args64()[reg_vars].boxed()); + } + + let mut out: VecDeque = VecDeque::new(); + + for node in &block.nodes { + let compiled = node.compile(registry); + out.extend(compiled); + out.push_back(Instr::with1(Mnemonic::Debug, Operand::Debug(format!("{}", node.dump())))); + } + + registry.block = None; + + let mut out = VecDeque::from(Vec::from(out) + .optimize() + .optimize() + ); + + out.extend(x64BuildEpilog(&block, registry)); + + for epAsm in x64BuildProlog(&block, registry) { + out.push_front(epAsm); + } + + Vec::from(out) + +} \ No newline at end of file diff --git a/src/Target/x64/compilation/prolog.rs b/src/Target/x64/compilation/prolog.rs new file mode 100644 index 00000000..f571f447 --- /dev/null +++ b/src/Target/x64/compilation/prolog.rs @@ -0,0 +1,36 @@ +use super::*; + +pub(crate) fn x64BuildProlog(_: &Block, registry: &mut TargetBackendDescr) -> Vec { + let mut res = vec![]; + + if registry.backend.currStackOffsetForLocalVars != 0 || registry.backend.stackSafe { + res.push( Instr::with1(Mnemonic::Push, Operand::Reg(x64Reg::Rbp.boxed())) ); + res.push( Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::Rbp.boxed()), Operand::Reg(x64Reg::Rsp.boxed())) ); + res.push( Instr::with2(Mnemonic::Sub, Operand::Reg(x64Reg::Rsp.boxed()), Operand::Imm(registry.backend.shadow + 8)) ); + } + + for backuped in ®istry.backend.saveRegister { + res.push( Instr::with1(Mnemonic::Push, Operand::Reg(backuped.boxed())) ) + } + + res.reverse(); + + res +} + +pub(crate) fn x64BuildEpilog(_: &Block, registry: &mut TargetBackendDescr) -> Vec { + let mut res = vec![]; + + for backuped in ®istry.backend.saveRegister { + res.push( Instr::with1(Mnemonic::Pop, Operand::Reg(backuped.boxed())) ) + } + + if registry.backend.currStackOffsetForLocalVars != 0 || registry.backend.stackSafe { + res.push( Instr::with2(Mnemonic::Add, Operand::Reg(x64Reg::Rsp.boxed()), Operand::Imm(registry.backend.shadow + 8)) ); + res.push( Instr::with1(Mnemonic::Pop, Operand::Reg(x64Reg::Rbp.boxed())) ); + } + + res.push( Instr::with0(Mnemonic::Ret)); + + res +} diff --git a/src/Target/x64/compilation/ret.rs b/src/Target/x64/compilation/ret.rs new file mode 100644 index 00000000..5a09b620 --- /dev/null +++ b/src/Target/x64/compilation/ret.rs @@ -0,0 +1,37 @@ +use super::*; + +pub(crate) fn CompileRetType(ret: &Return, registry: &mut TargetBackendDescr) -> Vec { + if ret.inner1 != Type::Void { + vec![Instr::with2(Mnemonic::Mov, match ret.inner1.into() { + TypeMetadata::u16 | TypeMetadata::i16 => Operand::Reg(registry.call.ret16().boxed()), + TypeMetadata::u32 | TypeMetadata::i32 => Operand::Reg(registry.call.ret32().boxed()), + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => Operand::Reg(registry.call.ret64().boxed()), + _ => unreachable!(), + }, Operand::Imm(ret.inner1.val() as i64))] + } else { + vec![] + } +} + +pub(crate) fn CompileRetVar(ret: &Return, registry: &mut TargetBackendDescr) -> Vec { + let (var, loc) = if let Some(loc) = registry.backend.varsStorage.get_key_value(&ret.inner1) { + loc.clone() + } else { + panic!("unknown variable: {:?}", ret.inner1) + }; + + if var.ty == TypeMetadata::Void { + return vec![]; + } + + vec![Instr::with2(Mnemonic::Mov, match var.ty { + TypeMetadata::u16 | TypeMetadata::i16 => Operand::Reg(registry.call.ret16().boxed()), + TypeMetadata::u32 | TypeMetadata::i32 => Operand::Reg(registry.call.ret32().boxed()), + TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => Operand::Reg(registry.call.ret64().boxed()), + _ => unreachable!(), + }, { + if let VarStorage::Memory(mem) = loc { Operand::Mem(mem.clone()) } + else if let VarStorage::Register(reg) = loc { Operand::Reg(reg.boxed()) } + else { unreachable!() } + })] +} diff --git a/src/Target/x64/ir.rs b/src/Target/x64/ir.rs deleted file mode 100644 index 3ef8ebb6..00000000 --- a/src/Target/x64/ir.rs +++ /dev/null @@ -1,716 +0,0 @@ -use std::collections::VecDeque; - -use crate::prelude::{Block, Function, Type, TypeMetadata, Var}; -use crate::Target::target_descr::{TargetBackendDescr, VarStorage}; -use crate::Target::Reg; -use crate::IR::{ir::*, Const}; -use super::Optimize; - -use crate::Target::CallConv; - -use super::{x64Reg, instr::*}; - -macro_rules! CompileMathVarVar { - ($name:ident, $node:ident, $mnemonic:expr) => { - pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { - let infos = &mut registry.backend; - let block = registry.block.unwrap(); - - let loc1 = if let Some(loc1) = infos.varsStorage.get(&node.inner1) { - loc1.clone() - } else { - panic!("unknown variable: {:?}", node.inner1) - }; - - let loc2 = if let Some(loc2) = infos.varsStorage.get(&node.inner2) { - loc2.clone() - - } else { - panic!("unknown variable: {:?}", node.inner1) - }; - - let boxed: Box = Box::new(node.clone()); - - if !block.isVarUsedAfterNode(&boxed, &node.inner1) { - infos.drop(&node.inner1); - } - if !block.isVarUsedAfterNode(&boxed, &node.inner2) { - infos.drop(&node.inner2); - } - if !block.isVarUsedAfterNode(&boxed, &node.inner3) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - let ty = &node.inner1.ty; - - let ret = { - if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { - VarStorage::Register(reg) - } else { - let addend = match ty { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => todo!("cant output an addition into an void"), - }; - - infos.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) - } - }; - - infos.insertVar( - node.inner3.clone(), - ret.clone() - ); - let tmp = infos.getTmpBasedOnTy(*ty); - - if let VarStorage::Register(loc1Reg) = &loc1 { - if let VarStorage::Register(loc2Reg) = &loc2 { - if let VarStorage::Register(reg) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc2Reg.boxed())), - Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), - ] - } else if let VarStorage::Memory(mem) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc2Reg.boxed())), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), - ]; - } else { todo!() } - } - } - - if let VarStorage::Memory(mem1) = loc1 { - if let VarStorage::Memory(mem2) = loc2 { - if let VarStorage::Register(reg) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Mem(mem2.clone())), - Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), - ]; - } else if let VarStorage::Memory(mem) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Mem(mem2.clone())), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), - ]; - } else { todo!() } - } - } - - todo!(); // nothing was compiled - } - }; -} - -macro_rules! CompileMathVarType { - ($name:ident, $node:ident, $mnemonic:expr) => { - pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { - let infos = &mut registry.backend; - let block = registry.block.unwrap(); - - let loc1 = if let Some(loc1) = infos.varsStorage.get(&node.inner1) { - loc1.clone() - } else { - panic!("unknown variable: {:?}", node.inner1) - }; - - let boxed: Box = Box::new(node.clone()); - - if !block.isVarUsedAfterNode(&boxed, &node.inner1) { - infos.drop(&node.inner1); - } - if !block.isVarUsedAfterNode(&boxed, &node.inner3) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - let ty = &node.inner1.ty; - - let ret = { - if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { - VarStorage::Register(reg) - } else { - let addend = match ty { - TypeMetadata::u16 | TypeMetadata::i16=> 2, - TypeMetadata::u32 | TypeMetadata::i32=> 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => todo!("cant output an addition into an void"), - }; - - infos.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) - } - }; - - infos.insertVar( - node.inner3.clone(), - ret.clone() - ); - let tmp = infos.getTmpBasedOnTy(*ty); - - if let VarStorage::Register(loc1Reg) = &loc1 { - if let VarStorage::Register(reg) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), - Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), - ] - } else if let VarStorage::Memory(mem) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Reg(loc1Reg.boxed())), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), - ]; - } else { todo!() } - } - - if let VarStorage::Memory(mem1) = loc1 { - if let VarStorage::Register(reg) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), - Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(tmp.boxed())), - ]; - } else if let VarStorage::Memory(mem) = &ret { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(tmp.boxed()), Operand::Mem(mem1.clone())), - Instr::with2($mnemonic, Operand::Reg(tmp.boxed()), Operand::Imm(node.inner2.val() as i64)), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(tmp.boxed())), - ]; - } else { todo!() } - } - - todo!(); // nothing was compiled - } - }; -} - -macro_rules! CompileMathTyTy { - ($name:ident, $node:ident, $op:tt) => { - pub(crate) fn $name(node: &$node, registry: &mut TargetBackendDescr) -> Vec { - let val = node.inner1.val() $op node.inner2.val(); - let block = registry.block.unwrap(); - - let boxed: Box = Box::new(node.clone()); - - if !block.isVarUsedAfterNode(&boxed, &node.inner3) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - CompileConstAssign(&ConstAssign::new(node.inner3.clone(), { - match node.inner3.ty { - TypeMetadata::u16 => Type::u16(val as u16), - TypeMetadata::u32 => Type::u32(val as u32), - TypeMetadata::u64 => Type::u64(val as u64), - TypeMetadata::i16 => Type::i16(val as i16), - TypeMetadata::i32 => Type::i32(val as i32), - TypeMetadata::i64 => Type::i64(val as i64), - TypeMetadata::ptr => Type::ptr(val as i64), - TypeMetadata::Void =>Type::Void, - } - }), registry) - } - } -} - -CompileMathVarVar!(CompileAddVarVar, Add, Mnemonic::Add); -CompileMathVarVar!(CompileSubVarVar, Sub, Mnemonic::Sub); -CompileMathVarVar!(CompileXorVarVar, Xor, Mnemonic::Xor); -CompileMathVarVar!(CompileOrVarVar, Or, Mnemonic::Or); -CompileMathVarVar!(CompileAndVarVar, And, Mnemonic::And); - -CompileMathVarType!(CompileAddVarTy, Add, Mnemonic::Add); -CompileMathVarType!(CompileSubVarTy, Sub, Mnemonic::Sub); -CompileMathVarType!(CompileXorVarTy, Xor, Mnemonic::Xor); -CompileMathVarType!(CompileOrVarTy, Or, Mnemonic::Or); -CompileMathVarType!(CompileAndVarTy, And, Mnemonic::And); - -CompileMathTyTy!(CompileAddTyTy, Add, +); -CompileMathTyTy!(CompileSubTyTy, Sub, -); -CompileMathTyTy!(CompileXorTyTy, Xor, ^); -CompileMathTyTy!(CompileOrTyTy, Or, |); -CompileMathTyTy!(CompileAndTyTy, And, &); - -pub(crate) fn CompileConstAssign(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { - let infos = &mut registry.backend; - let block = registry.block.unwrap(); - - let ty = &assign.inner1.ty; - - let boxed: Box = Box::new(assign.clone()); - - if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - let store = { - if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { - VarStorage::Register(reg) - } else { - let addend = match ty { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => todo!("cant output an assing somthing to void"), - }; - - infos.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) - } - }; - - infos.insertVar( - assign.inner1.clone(), - store.clone() - ); - - if let VarStorage::Register(reg) = &store { - vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Imm(assign.inner2.val() as i64)) ] - } else if let VarStorage::Memory(mem) = &store { - vec![ Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Imm(assign.inner2.val() as i64)) ] - } else { todo!() } -} - -pub(crate) fn CompileConstAssignVar(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { - let infos = &mut registry.backend; - let block = registry.block.unwrap(); - - let loc = if let Some(loc) = infos.varsStorage.get_key_value(&assign.inner2) { - loc.1.clone() - } else { - panic!("unknown variable: {:?}", assign.inner2) - }; - - let ty = &assign.inner1.ty; - - let boxed: Box = Box::new(assign.clone()); - - if !block.isVarUsedAfterNode(&boxed, &assign.inner2) { - infos.drop(&assign.inner2); - } - if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - let store = { - if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { - VarStorage::Register(reg) - } else { - let addend = match ty { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => todo!("cant output an assing somthing to void"), - }; - - infos.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) - } - }; - - infos.insertVar( - assign.inner1.clone(), - store.clone() - ); - - if let VarStorage::Register(reg) = &store { - if let VarStorage::Register(reg2) = &loc { - vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(reg2.boxed())) ] - } else if let VarStorage::Memory(mem2) = &loc { - vec![ Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Mem(mem2.clone())) ] - } else { unreachable!() } - } else if let VarStorage::Memory(mem) = &store { - if let VarStorage::Register(reg2) = &loc { - vec![ Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(reg2.boxed())) ] - } else if let VarStorage::Memory(mem2) = &loc { - vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(mem2.clone())), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem2.clone()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) - ] - } else { unreachable!() } - } else { todo!() } -} - -pub(crate) fn CompileConstAssignConst(assign: &ConstAssign, registry: &mut TargetBackendDescr) -> Vec { - let block = registry.block.unwrap(); - - registry.backend.stackSafe = true; - - let infos = &mut registry.backend; - - let ty = &assign.inner1.ty; - - let boxed: Box = Box::new(assign.clone()); - - if !block.isVarUsedAfterNode(&boxed, &assign.inner1) { - return vec![]; // all of these calculations don't need to be done: dead code removal - } - - let store = { - if let Some(reg) = infos.getOpenRegBasedOnTy(*ty) { - VarStorage::Register(reg) - } else { - let addend = match ty { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => todo!("cant output an assing somthing to void"), - }; - - infos.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (infos.currStackOffsetForLocalVars - addend) as u32) - } - }; - - infos.insertVar( - assign.inner1.clone(), - store.clone() - ); - - if let VarStorage::Register(reg) = &store { - vec![ - Instr::with2(Mnemonic::Lea, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(MemOp { base: None, index: None, scale: 0, displ: 1, rip: true })), - Instr::with1(Mnemonic::Link, Operand::LinkDestination(assign.inner2.name.to_string(), -4)), - Instr::with2(Mnemonic::Mov, Operand::Reg(reg.boxed()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) - ] - } else if let VarStorage::Memory(mem) = &store { - vec![ - Instr::with2(Mnemonic::Lea, Operand::Reg(infos.getTmpBasedOnTy(*ty)), Operand::Mem(MemOp { base: None, index: None, scale: 0, displ: 1, rip: true })), - Instr::with1(Mnemonic::Link, Operand::LinkDestination(assign.inner2.name.to_string(), -4)), - Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(infos.getTmpBasedOnTy(*ty))) - ] - } else { todo!() } -} - -pub(crate) fn CompileRetType(ret: &Return, registry: &mut TargetBackendDescr) -> Vec { - if ret.inner1 != Type::Void { - vec![Instr::with2(Mnemonic::Mov, match ret.inner1.into() { - TypeMetadata::u16 | TypeMetadata::i16 => Operand::Reg(registry.call.ret16().boxed()), - TypeMetadata::u32 | TypeMetadata::i32 => Operand::Reg(registry.call.ret32().boxed()), - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => Operand::Reg(registry.call.ret64().boxed()), - _ => unreachable!(), - }, Operand::Imm(ret.inner1.val() as i64))] - } else { - vec![] - } -} - -pub(crate) fn CompileRetVar(ret: &Return, registry: &mut TargetBackendDescr) -> Vec { - let (var, loc) = if let Some(loc) = registry.backend.varsStorage.get_key_value(&ret.inner1) { - loc.clone() - } else { - panic!("unknown variable: {:?}", ret.inner1) - }; - - if var.ty == TypeMetadata::Void { - return vec![]; - } - - vec![Instr::with2(Mnemonic::Mov, match var.ty { - TypeMetadata::u16 | TypeMetadata::i16 => Operand::Reg(registry.call.ret16().boxed()), - TypeMetadata::u32 | TypeMetadata::i32 => Operand::Reg(registry.call.ret32().boxed()), - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => Operand::Reg(registry.call.ret64().boxed()), - _ => unreachable!(), - }, { - if let VarStorage::Memory(mem) = loc { Operand::Mem(mem.clone()) } - else if let VarStorage::Register(reg) = loc { Operand::Reg(reg.boxed()) } - else { unreachable!() } - })] -} - -pub(crate) fn CompileCast(cast: &Cast, registry: &mut TargetBackendDescr) -> Vec { - let boxed: Box = Box::new(cast.clone()); - let block = registry.block.unwrap(); - - let loc = if let Some(loc) = registry.backend.varsStorage.get(&cast.inner1) { - loc.clone() - } else { - panic!("unknown variable: {:?}", cast.inner1) - }; - - if block.isVarUsedAfterNode(&boxed, &cast.inner1) { - registry.backend.drop(&cast.inner1); - } - if block.isVarUsedAfterNode(&boxed, &cast.inner3) { - return vec![]; - } - - let store = { - if let Some(reg) = registry.backend.getOpenRegBasedOnTy(cast.inner2) { - VarStorage::Register(reg) - } else { - let addend = match cast.inner2 { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 => 8, - TypeMetadata::Void => todo!("cant cast into void"), - TypeMetadata::ptr => todo!("cant cast into ptr"), - }; - - registry.backend.currStackOffsetForLocalVars += addend; - VarStorage::Memory(x64Reg::Rbp - (registry.backend.currStackOffsetForLocalVars - addend) as u32) - } - }; - - registry.backend.insertVar( - cast.inner3.clone(), - store.clone() - ); - - match loc { - VarStorage::Register(inbound) => { - if let VarStorage::Register(outboud) = store { - if cast.inner1.ty.bitSize() > cast.inner3.ty.bitSize() { - if inbound.is_gr16() || inbound.is_gr8() { // zero extend - return vec![ - Instr::with2(Mnemonic::Movzx, Operand::Reg(outboud), Operand::Reg(inbound)), - ]; - } else { - return vec![ - Instr::with2(Mnemonic::Mov, Operand::Reg(outboud), Operand::Reg(inbound)) - ]; - } - } else { - return vec![{ - if inbound.is_gr64() { - Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub64()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub64()).unwrap().boxed())) - } else if inbound.is_gr32() { - Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub32()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub32()).unwrap().boxed())) - } else if inbound.is_gr16() { - Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub16()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub16()).unwrap().boxed())) - } else if inbound.is_gr8() { - Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::parse(outboud.sub8()).unwrap().boxed()), Operand::Reg(x64Reg::parse(inbound.sub8()).unwrap().boxed())) - } else { panic!() } - }]; - } - } - }, - VarStorage::Memory(_) => todo!(), - } - - vec![] -} - -pub(crate) fn CompileCall(call: &Call, Var>, registry: &mut TargetBackendDescr) -> Vec { - let boxed: Box = Box::new(call.clone()); - let block = registry.block.unwrap(); - - let mut asm = vec![]; - - for reg in vec![x64Reg::Rcx, x64Reg::Rdx, x64Reg::Rsi, x64Reg::Rdi, x64Reg::Rsi] { // save mutable registers - if !registry.backend.openUsableRegisters64.contains(®.boxed()) { - let var = registry.backend.getVarByReg(reg.boxed()).cloned(); - - if let Some(var) = var { - if block.isVarUsedAfterNode(&boxed, &var) { - asm.push(Instr::with1(Mnemonic::Push, Operand::Reg(reg.boxed()))); - } - } - } - } - - let func = &call.inner1; - - let mut reg_args = 0; - - for arg in &call.inner2 { - let loc = registry.backend.varsStorage.get_key_value(arg).expect("expected valid variable as arg input"); - - match loc.1 { - VarStorage::Register(reg) => { - if reg_args < registry.call.regArgs() { - match arg.ty { - TypeMetadata::i64 | TypeMetadata::u64 | TypeMetadata::ptr => - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args64()[reg_args].boxed()), Operand::Reg(reg.clone()))), - TypeMetadata::i32 | TypeMetadata::u32 => - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args32()[reg_args].boxed()), Operand::Reg(reg.clone()))), - TypeMetadata::i16 | TypeMetadata::u16 => - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args16()[reg_args].boxed()), Operand::Reg(reg.clone()))), - TypeMetadata::Void => {}, - } - - reg_args += 1; - } else { - asm.push( Instr::with1(Mnemonic::Push, Operand::Reg(reg.clone()))); - } - }, - VarStorage::Memory(mem) => { - if reg_args < registry.call.regArgs() { - if arg.ty == TypeMetadata::i64 || arg.ty == TypeMetadata::u64 { - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args64()[reg_args].boxed()), Operand::Mem(mem.clone()))); - } else if arg.ty == TypeMetadata::i32 || arg.ty == TypeMetadata::u32 { - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args32()[reg_args].boxed()), Operand::Mem(mem.clone()))); - } else if arg.ty == TypeMetadata::i16 || arg.ty == TypeMetadata::u16 { - asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(registry.call.args16()[reg_args].boxed()), Operand::Mem(mem.clone()))); - } - reg_args += 1; - } else { - asm.push( Instr::with1(Mnemonic::Push, Operand::Mem(mem.clone()))); - } - }, - } - if !block.isVarUsedAfterNode(&boxed, arg) { - registry.backend.drop(arg); - } - } - - if registry.call.reset_eax() { - asm.push(Instr::with2(Mnemonic::Xor, Operand::Reg(x64Reg::Eax.boxed()), Operand::Reg(x64Reg::Eax.boxed()))); - } - - asm.push( Instr::with1(Mnemonic::Call, Operand::Imm(0))); - - asm.push( Instr::with1(Mnemonic::Link, Operand::LinkDestination(call.inner1.name.to_string(), -4))); - - if func.ty.ret != TypeMetadata::Void { - let store = if let Some(reg) = registry.backend.getOpenRegBasedOnTy(call.inner3.ty) { - match func.ty.ret { - TypeMetadata::u16 | TypeMetadata::i16 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret16().boxed())) ), - TypeMetadata::u32 | TypeMetadata::i32 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret32().boxed())) ), - TypeMetadata::u64 | TypeMetadata::i64 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Reg(reg.clone()), Operand::Reg(registry.call.ret64().boxed())) ), - _ => unreachable!(), - }; - VarStorage::Register(reg) - } else { - let addend = match call.inner3.ty { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => unreachable!(), - }; - - registry.backend.currStackOffsetForLocalVars += addend; - let mem = x64Reg::Rbp - (registry.backend.currStackOffsetForLocalVars - addend) as u32; - - match func.ty.ret { - TypeMetadata::u16 | TypeMetadata::i16 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret16().boxed())) ), - TypeMetadata::u32 | TypeMetadata::i32 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret32().boxed())) ), - TypeMetadata::u64 | TypeMetadata::i64 => asm.push( Instr::with2(Mnemonic::Mov, Operand::Mem(mem.clone()), Operand::Reg(registry.call.ret64().boxed())) ), - _ => unreachable!(), - }; - - VarStorage::Memory(mem) - }; - - registry.backend.insertVar(call.inner3.clone(), store); - } - - for reg in vec![x64Reg::Rcx, x64Reg::Rdx, x64Reg::Rsi, x64Reg::Rdi, x64Reg::Rsi] { // getback mutable registers - if !registry.backend.openUsableRegisters64.contains(®.boxed()) { - let var = registry.backend.getVarByReg(reg.boxed()).cloned(); - - if let Some(var) = var { - if block.isVarUsedAfterNode(&boxed, &var) { - asm.push(Instr::with1(Mnemonic::Pop, Operand::Reg(reg.boxed()))); - } - } - } - } - - asm -} - -pub(crate) fn x64BuildProlog(_: &Block, registry: &mut TargetBackendDescr) -> Vec { - let mut res = vec![]; - - if registry.backend.currStackOffsetForLocalVars != 0 || registry.backend.stackSafe { - res.push( Instr::with1(Mnemonic::Push, Operand::Reg(x64Reg::Rbp.boxed())) ); - res.push( Instr::with2(Mnemonic::Mov, Operand::Reg(x64Reg::Rbp.boxed()), Operand::Reg(x64Reg::Rsp.boxed())) ); - res.push( Instr::with2(Mnemonic::Sub, Operand::Reg(x64Reg::Rsp.boxed()), Operand::Imm(registry.backend.shadow + 8)) ); - } - - for backuped in ®istry.backend.saveRegister { - res.push( Instr::with1(Mnemonic::Push, Operand::Reg(backuped.boxed())) ) - } - - res.reverse(); - - res -} - -pub(crate) fn x64BuildEpilog(_: &Block, registry: &mut TargetBackendDescr) -> Vec { - let mut res = vec![]; - - for backuped in ®istry.backend.saveRegister { - res.push( Instr::with1(Mnemonic::Pop, Operand::Reg(backuped.boxed())) ) - } - - if registry.backend.currStackOffsetForLocalVars != 0 || registry.backend.stackSafe { - res.push( Instr::with2(Mnemonic::Add, Operand::Reg(x64Reg::Rsp.boxed()), Operand::Imm(registry.backend.shadow + 8)) ); - res.push( Instr::with1(Mnemonic::Pop, Operand::Reg(x64Reg::Rbp.boxed())) ); - } - - res.push( Instr::with0(Mnemonic::Ret)); - - res -} - -pub(crate) fn buildAsmX86<'a>(block: &'a Block, func: &Function, call: &CallConv, registry: &mut TargetBackendDescr<'a>) -> Vec { - registry.block = Some(&block); - - let info = &mut registry.backend; - - let mut reg_vars = 0; - let mut stack_off = 8; // because in an call the return adress gets pushed which is 8 bytes long - let mut var_index = 0; - - for (_, meta) in &func.ty.args { - let mut var = Var(&mut block.clone(), meta.to_owned()); - var.name = format!("%{}", var_index); - - info.insertVar(var, { - if reg_vars >= call.regArgs() { - let addend = match meta { - TypeMetadata::u16 | TypeMetadata::i16 => 2, - TypeMetadata::u32 | TypeMetadata::i32 => 4, - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => 8, - TypeMetadata::Void => continue, - }; - - stack_off += addend; - VarStorage::Memory(x64Reg::Rbp - (stack_off - addend)) - } else { - reg_vars += 1; - VarStorage::Register( match meta { - TypeMetadata::u16 | TypeMetadata::i16 => call.args16()[reg_vars - 1].boxed(), - TypeMetadata::u32 | TypeMetadata::i32 => call.args32()[reg_vars - 1].boxed(), - TypeMetadata::u64 | TypeMetadata::i64 | TypeMetadata::ptr => call.args64()[reg_vars - 1].boxed(), - TypeMetadata::Void => continue, - }) - } - }); - - var_index += 1; - } - - if reg_vars < call.regArgs() { - info.dropReg(call.args64()[reg_vars].boxed()); - } - - let mut out: VecDeque = VecDeque::new(); - - for node in &block.nodes { - let compiled = node.compile(registry); - out.extend(compiled); - out.push_back(Instr::with1(Mnemonic::Debug, Operand::Debug(format!("{}", node.dump())))); - } - - registry.block = None; - - let mut out = VecDeque::from(Vec::from(out) - .optimize() - .optimize() - ); - - out.extend(x64BuildEpilog(&block, registry)); - - for epAsm in x64BuildProlog(&block, registry) { - out.push_front(epAsm); - } - - Vec::from(out) - -} \ No newline at end of file diff --git a/src/Target/x64/mod.rs b/src/Target/x64/mod.rs index e6a5680d..775ec4fb 100644 --- a/src/Target/x64/mod.rs +++ b/src/Target/x64/mod.rs @@ -2,13 +2,14 @@ use std::collections::VecDeque; -use ir::*; +pub(crate) mod compilation; + +use compilation::*; use super::{CallConv, Lexer, Reg, TargetBackendDescr}; mod reg; pub use reg::*; -pub(crate) mod ir; pub(crate) mod call; mod asm;