more codegen

This commit is contained in:
nora 2023-05-24 21:49:56 +02:00
parent ee0b311261
commit db219d3d74
7 changed files with 214 additions and 15 deletions

1
Cargo.lock generated
View file

@ -109,6 +109,7 @@ dependencies = [
"iced-x86",
"object",
"parser",
"rustc-hash",
]
[[package]]

View file

@ -104,7 +104,7 @@ pub struct RegisterData<'cx> {
pub name: Option<Symbol>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Register(pub u32);
#[derive(Debug, Clone)]
@ -245,6 +245,14 @@ impl ConstValue {
pub fn u64(int: u64) -> Self {
Self::Int(int.into())
}
pub fn as_i32(self) -> i32 {
match self {
Self::StaticPtr(_) => panic!("StaticPtr cannot be converted to integer"),
Self::Void => panic!("Void cannot be converted to integer"),
Self::Int(int) => int.try_into().unwrap(),
}
}
}
impl Operand {

View file

@ -184,6 +184,8 @@ impl<W: Write> PrettyPrinter<W> {
}
}
writeln!(self.out, "}}")?;
Ok(())
}
}

View file

@ -18,3 +18,4 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [
] }
object = { version = "0.31.1", features = ["write"] }
parser = { path = "../parser" }
rustc-hash = "1.1.0"

View file

@ -10,7 +10,7 @@ use object::{
type Result<T, E = analysis::Error> = std::result::Result<T, E>;
pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> {
pub fn generate<'cx>(lcx: &'cx LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> {
let mut obj = Object::new(
object::BinaryFormat::Elf,
object::Architecture::X86_64,
@ -61,11 +61,11 @@ pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> {
"linking with `cc` failed"
)));
} else {
std::fs::remove_file("main.o").map_err(|err| {
analysis::Error::new_without_span(format!(
"failed to remove temporary file main.o: {err}"
))
})?;
// std::fs::remove_file("main.o").map_err(|err| {
// analysis::Error::new_without_span(format!(
// "failed to remove temporary file main.o: {err}"
// ))
// })?;
}
Ok(())

View file

@ -1,6 +1,13 @@
use analysis::{ir::Func, LoweringCx};
use iced_x86::{code_asm as x, IcedError};
use analysis::{
ir::{BbIdx, ConstValue, Func, Operand, Register, Statement, StatementKind},
LoweringCx,
};
use iced_x86::{
code_asm::{self as x, CodeAssembler},
IcedError, Instruction,
};
use parser::Span;
use rustc_hash::FxHashMap;
use crate::Result;
@ -17,16 +24,127 @@ impl<T> IcedErrExt for Result<T, IcedError> {
}
}
pub fn generate_func<'cx>(_lcx: &LoweringCx<'cx>, func: &Func<'cx>) -> Result<Vec<u8>> {
#[derive(Debug, Clone, Copy)]
enum RegValue {
Stack { offset: u64 },
}
struct AsmCtxt<'cx> {
lcx: &'cx LoweringCx<'cx>,
a: CodeAssembler,
reg_map: FxHashMap<Register, RegValue>,
current_stack_offset: u64,
bb_idx: BbIdx,
}
impl<'cx> AsmCtxt<'cx> {
fn generate_func(&mut self, func: &Func<'cx>) -> Result<()> {
// TODO: Prologue
loop {
let bb = &func.bbs[self.bb_idx.as_usize()];
for stmt in &bb.statements {
let Statement {
span: st_sp,
ref kind,
} = *stmt;
match *kind {
StatementKind::Alloca {
reg,
size,
align: _,
} => {
// TODO: Align
match size {
Operand::Const(c) => {
let offset = c.as_i32();
self.a.sub(x::rsp, offset).sp(st_sp)?;
self.current_stack_offset += offset as u64;
}
Operand::Reg(_) => todo!("dynamic alloca is not supported"),
};
self.reg_map.insert(
reg,
RegValue::Stack {
offset: self.current_stack_offset,
},
);
}
StatementKind::Store {
ptr,
value,
size,
align,
} => match ptr {
Operand::Const(_) => todo!("const stores not implemented"),
Operand::Reg(reg) => {
let value = self.reg_map[&reg];
let stack_offset = match value {
RegValue::Stack { offset } => offset,
};
//let rhs = match value {
// Operand::Const(c) => {}
// Operand::Reg(reg) => {}
//};
// mov [rbp + OFFSET], RHS
//self.a.add_instruction(Instruction::with2(Code::Mov, op0, op1))
self.a.mov(x::ptr(x::rax), x::rbx);
}
},
StatementKind::Load {
result,
ptr,
size,
align,
} => todo!(),
StatementKind::BinOp {
kind,
lhs,
rhs,
result,
} => todo!(),
StatementKind::UnaryOperation { rhs, kind, result } => todo!(),
StatementKind::PtrOffset {
result,
reg,
amount,
} => todo!(),
StatementKind::Call {
result,
func,
ref args,
} => todo!(),
}
}
todo!("next bbs");
}
Ok(())
}
}
pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result<Vec<u8>> {
assert_eq!(func.arity, 0, "arguments??? in MY uwucc????");
let sp = func.def_span;
let mut a = x::CodeAssembler::new(64).sp(sp)?;
let fn_sp = func.def_span;
let mut a = CodeAssembler::new(64).sp(fn_sp)?;
a.xor(x::rax, x::rax).sp(sp)?;
a.ret().sp(sp)?;
let mut cx = AsmCtxt {
lcx,
a,
reg_map: FxHashMap::default(),
current_stack_offset: 0,
bb_idx: BbIdx(0),
};
let code = a.assemble(0x4000).sp(sp)?;
cx.generate_func(func)?;
let code = cx.a.assemble(0x4000).sp(fn_sp)?;
Ok(code)
}

69
notes.md Normal file
View file

@ -0,0 +1,69 @@
# lowering
This C program
```c
long main() {
long a = 0;
int b = 0;
return 0;
}
```
should lower into the following unoptimized IR
```llvmir
def main() {
bb0:
%a = alloca, size=8, align=8
store %a, 0, size=8, align=8
%b = alloca, size=4, align=4
store %b, 0, size=4, align=4
ret 0
}
```
this IR can then be lowered to very sane machine code ignoring stack alignment which also like matters
```x86asm
sub rbp, 8 ; a
mov [rbp], 0
sub rbp, 4 ; b
xor rax, rax
ret
```
---
```c
int main()
{
int a = 1 + 3 * 4;
return 0;
}
```
```llvmir
def main() {
bb0:
%a = alloca, size=4, align=4
%1 = mul 3, 4
%2 = add 1, %1
store %a, %2, size=4, align=4
ret 0
}
```
```x86asm
sub rbp, 4 ; a
sub rbp, 4 ; %1
mov rbx, 3
mul rbx, 4
mov dword ptr [rbp], rbx
sub rbp, 4 ; %2
mov rbx, 1
add rbx, dword ptr [rbp + 4]
mov dword ptr [rbp + 8], dword ptr [rbp]
xor rax, rax
ret
```