mirror of
https://github.com/Noratrieb/uwucc.git
synced 2026-01-14 16:45:07 +01:00
more codegen
This commit is contained in:
parent
ee0b311261
commit
db219d3d74
7 changed files with 214 additions and 15 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -109,6 +109,7 @@ dependencies = [
|
||||||
"iced-x86",
|
"iced-x86",
|
||||||
"object",
|
"object",
|
||||||
"parser",
|
"parser",
|
||||||
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ pub struct RegisterData<'cx> {
|
||||||
pub name: Option<Symbol>,
|
pub name: Option<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Register(pub u32);
|
pub struct Register(pub u32);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -245,6 +245,14 @@ impl ConstValue {
|
||||||
pub fn u64(int: u64) -> Self {
|
pub fn u64(int: u64) -> Self {
|
||||||
Self::Int(int.into())
|
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 {
|
impl Operand {
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,8 @@ impl<W: Write> PrettyPrinter<W> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeln!(self.out, "}}")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,3 +18,4 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
object = { version = "0.31.1", features = ["write"] }
|
object = { version = "0.31.1", features = ["write"] }
|
||||||
parser = { path = "../parser" }
|
parser = { path = "../parser" }
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use object::{
|
||||||
|
|
||||||
type Result<T, E = analysis::Error> = std::result::Result<T, E>;
|
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(
|
let mut obj = Object::new(
|
||||||
object::BinaryFormat::Elf,
|
object::BinaryFormat::Elf,
|
||||||
object::Architecture::X86_64,
|
object::Architecture::X86_64,
|
||||||
|
|
@ -61,11 +61,11 @@ pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> {
|
||||||
"linking with `cc` failed"
|
"linking with `cc` failed"
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
std::fs::remove_file("main.o").map_err(|err| {
|
// std::fs::remove_file("main.o").map_err(|err| {
|
||||||
analysis::Error::new_without_span(format!(
|
// analysis::Error::new_without_span(format!(
|
||||||
"failed to remove temporary file main.o: {err}"
|
// "failed to remove temporary file main.o: {err}"
|
||||||
))
|
// ))
|
||||||
})?;
|
// })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
use analysis::{ir::Func, LoweringCx};
|
use analysis::{
|
||||||
use iced_x86::{code_asm as x, IcedError};
|
ir::{BbIdx, ConstValue, Func, Operand, Register, Statement, StatementKind},
|
||||||
|
LoweringCx,
|
||||||
|
};
|
||||||
|
use iced_x86::{
|
||||||
|
code_asm::{self as x, CodeAssembler},
|
||||||
|
IcedError, Instruction,
|
||||||
|
};
|
||||||
use parser::Span;
|
use parser::Span;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::Result;
|
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[®];
|
||||||
|
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????");
|
assert_eq!(func.arity, 0, "arguments??? in MY uwucc????");
|
||||||
|
|
||||||
let sp = func.def_span;
|
let fn_sp = func.def_span;
|
||||||
let mut a = x::CodeAssembler::new(64).sp(sp)?;
|
let mut a = CodeAssembler::new(64).sp(fn_sp)?;
|
||||||
|
|
||||||
a.xor(x::rax, x::rax).sp(sp)?;
|
let mut cx = AsmCtxt {
|
||||||
a.ret().sp(sp)?;
|
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)
|
Ok(code)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
notes.md
Normal file
69
notes.md
Normal 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
|
||||||
|
```
|
||||||
Loading…
Add table
Add a link
Reference in a new issue