From ee0b311261677b3ceabfe8f32b74d1331aff2b6d Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 24 May 2023 20:24:19 +0200 Subject: [PATCH] codegen --- .gitignore | 9 ++-- Cargo.lock | 107 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- analysis/src/ctxt.rs | 4 +- analysis/src/lib.rs | 17 +++++-- analysis/src/lower.rs | 4 +- codegen/Cargo.toml | 20 ++++++++ codegen/src/lib.rs | 72 +++++++++++++++++++++++++++ codegen/src/x86.rs | 32 ++++++++++++ codegen_x86/Cargo.toml | 8 --- codegen_x86/src/lib.rs | 6 --- src/main.rs | 19 +++++--- 12 files changed, 265 insertions(+), 36 deletions(-) create mode 100644 codegen/Cargo.toml create mode 100644 codegen/src/lib.rs create mode 100644 codegen/src/x86.rs delete mode 100644 codegen_x86/Cargo.toml delete mode 100644 codegen_x86/src/lib.rs diff --git a/.gitignore b/.gitignore index aaeeabc..e08cc7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ /target -.idea -.vscode +/.idea +/.vscode # for testing things -_*.c \ No newline at end of file +/_*.c +# any object files and a.out +*.o +*.out \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3571d40..01ceab9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e6e951cfbb2db8de1828d49073a113a29fd7117b1596caa781a258c7e38d72" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -72,6 +83,12 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.73" @@ -85,8 +102,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "codegen_x86" +name = "codegen" version = "0.1.0" +dependencies = [ + "analysis", + "iced-x86", + "object", + "parser", +] [[package]] name = "console" @@ -178,7 +201,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -187,6 +210,24 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.0", +] + +[[package]] +name = "iced-x86" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" +dependencies = [ + "lazy_static", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -283,6 +324,20 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown 0.13.2", + "indexmap", + "memchr", + "ruzstd", +] + [[package]] name = "once_cell" version = "1.12.0" @@ -402,6 +457,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ruzstd" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a15e661f0f9dac21f3494fe5d23a6338c0ac116a2d22c2b63010acd89467ffe" +dependencies = [ + "byteorder", + "thiserror", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.10" @@ -484,6 +550,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.98" @@ -538,6 +610,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.11" @@ -549,6 +641,16 @@ dependencies = [ "num_threads", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "unicode-ident" version = "1.0.1" @@ -576,6 +678,7 @@ version = "0.1.0" dependencies = [ "analysis", "bumpalo", + "codegen", "dbg-pls", "parser", ] diff --git a/Cargo.toml b/Cargo.toml index 406ada9..fb08856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [".", "./parser", "./analysis", "./codegen_x86"] +members = [".", "./parser", "./analysis", "./codegen"] [package] name = "uwucc" @@ -11,5 +11,6 @@ edition = "2021" [dependencies] bumpalo = "3.10.0" analysis = { path = "./analysis" } +codegen = { path = "./codegen" } parser = { path = "./parser" } dbg-pls = { version = "0.3.2", features = ["derive", "colors"] } diff --git a/analysis/src/ctxt.rs b/analysis/src/ctxt.rs index 76d5c1c..f61d0e0 100644 --- a/analysis/src/ctxt.rs +++ b/analysis/src/ctxt.rs @@ -12,7 +12,7 @@ use crate::{ }; #[derive(Debug)] -pub(crate) struct LoweringCx<'cx> { +pub struct LoweringCx<'cx> { tys: RefCell>>, layouts: RefCell>, string_literals: RefCell>, @@ -23,7 +23,7 @@ pub(crate) struct LoweringCx<'cx> { } impl<'cx> LoweringCx<'cx> { - pub(crate) fn new(arena: &'cx bumpalo::Bump) -> Self { + pub fn new(arena: &'cx bumpalo::Bump) -> Self { LoweringCx { tys: RefCell::default(), layouts: RefCell::default(), diff --git a/analysis/src/lib.rs b/analysis/src/lib.rs index ea45723..78aecf4 100644 --- a/analysis/src/lib.rs +++ b/analysis/src/lib.rs @@ -2,17 +2,18 @@ #![warn(rust_2018_idioms)] mod ctxt; -mod ir; +pub mod ir; mod lower; -mod ty; +pub mod ty; +pub use ctxt::LoweringCx; pub use lower::lower_translation_unit; use parser::Span; #[derive(Debug)] pub struct Error { msg: String, - span: Span, + span: Option, notes: Vec, } @@ -26,7 +27,15 @@ impl Error { pub fn new(msg: impl Into, span: Span) -> Self { Self { msg: msg.into(), - span, + span: Some(span), + notes: Vec::new(), + } + } + + pub fn new_without_span(msg: impl Into) -> Self { + Self { + msg: msg.into(), + span: None, notes: Vec::new(), } } diff --git a/analysis/src/lower.rs b/analysis/src/lower.rs index ac363b3..703de1f 100644 --- a/analysis/src/lower.rs +++ b/analysis/src/lower.rs @@ -21,11 +21,9 @@ use crate::{ type Result = std::result::Result; pub fn lower_translation_unit<'cx>( - arena: &'cx bumpalo::Bump, + lcx: &mut LoweringCx<'cx>, ast: &ast::TranslationUnit, ) -> Result, Error> { - let mut lcx = LoweringCx::new(arena); - let mut ir = Ir { funcs: FxHashMap::default(), }; diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml new file mode 100644 index 0000000..df34498 --- /dev/null +++ b/codegen/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "codegen" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +analysis = { path = "../analysis" } +iced-x86 = { version = "1.18.0", default-features = false, features = [ + "encoder", + "block_encoder", + "op_code_info", + "instr_info", + "nasm", # for debugging output + "code_asm", + "std", +] } +object = { version = "0.31.1", features = ["write"] } +parser = { path = "../parser" } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs new file mode 100644 index 0000000..461b3bc --- /dev/null +++ b/codegen/src/lib.rs @@ -0,0 +1,72 @@ +mod x86; + +use std::process::Stdio; + +use analysis::{ir::Ir, LoweringCx}; +use object::{ + elf, + write::{Object, Symbol}, +}; + +type Result = std::result::Result; + +pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> { + let mut obj = Object::new( + object::BinaryFormat::Elf, + object::Architecture::X86_64, + object::Endianness::Little, + ); + + let text = obj.add_section(Vec::new(), b".text".to_vec(), object::SectionKind::Text); + + for (_def_id, func) in &ir.funcs { + let code = x86::generate_func(lcx, func)?; + + let offset = obj.append_section_data(text, &code, 8); + + let sym = Symbol { + name: func.name.as_str(|s| s.as_bytes().to_vec()), + value: offset, + size: code.len().try_into().unwrap(), + kind: object::SymbolKind::Text, + scope: object::SymbolScope::Linkage, + weak: false, + section: object::write::SymbolSection::Section(text), + flags: object::SymbolFlags::Elf { + st_info: ((elf::STB_GLOBAL) << 4) | (elf::STT_FUNC), + st_other: elf::STV_DEFAULT, + }, + }; + + obj.add_symbol(sym); + } + + let object_file = obj.write().map_err(|err| { + analysis::Error::new_without_span(format!("failed to create object file: {err}")) + })?; + + std::fs::write("main.o", object_file).map_err(|err| { + analysis::Error::new_without_span(format!("failed to write object file main.o: {err}")) + })?; + + let output = std::process::Command::new("cc") + .arg("main.o") + .stdout(Stdio::inherit()) + .stdout(Stdio::inherit()) + .output() + .map_err(|err| analysis::Error::new_without_span(format!("failed to spawn `cc`: {err}")))?; + + if !output.status.success() { + return Err(analysis::Error::new_without_span(format!( + "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}" + )) + })?; + } + + Ok(()) +} diff --git a/codegen/src/x86.rs b/codegen/src/x86.rs new file mode 100644 index 0000000..025380e --- /dev/null +++ b/codegen/src/x86.rs @@ -0,0 +1,32 @@ +use analysis::{ir::Func, LoweringCx}; +use iced_x86::{code_asm as x, IcedError}; +use parser::Span; + +use crate::Result; + +trait IcedErrExt { + type T; + fn sp(self, span: Span) -> Result; +} + +impl IcedErrExt for Result { + type T = T; + + fn sp(self, span: Span) -> Result { + self.map_err(|e| analysis::Error::new(e.to_string(), span)) + } +} + +pub fn generate_func<'cx>(_lcx: &LoweringCx<'cx>, func: &Func<'cx>) -> Result> { + assert_eq!(func.arity, 0, "arguments??? in MY uwucc????"); + + let sp = func.def_span; + let mut a = x::CodeAssembler::new(64).sp(sp)?; + + a.xor(x::rax, x::rax).sp(sp)?; + a.ret().sp(sp)?; + + let code = a.assemble(0x4000).sp(sp)?; + + Ok(code) +} diff --git a/codegen_x86/Cargo.toml b/codegen_x86/Cargo.toml deleted file mode 100644 index 08431c5..0000000 --- a/codegen_x86/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "codegen_x86" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/codegen_x86/src/lib.rs b/codegen_x86/src/lib.rs deleted file mode 100644 index fafa470..0000000 --- a/codegen_x86/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(dead_code)] // TODO: no -#![warn(rust_2018_idioms)] - -pub fn generate() { - println!("ud2"); -} diff --git a/src/main.rs b/src/main.rs index fe8b31b..2bddb09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use analysis::LoweringCx; + fn main() { let input_file = std::env::args().nth(1).expect("first argument"); let src = std::fs::read_to_string(&input_file).unwrap_or_else(|err| { @@ -16,12 +18,15 @@ fn main() { println!("// END CODE -------------------"); let arena = bumpalo::Bump::new(); + let mut lcx = LoweringCx::new(&arena); - let ir = analysis::lower_translation_unit(&arena, &ast); - match ir { - Ok(_) => {} - Err(err) => { - dbg!(err); - } - } + let ir = analysis::lower_translation_unit(&mut lcx, &ast).unwrap_or_else(|err| { + dbg!(err); + std::process::exit(1); + }); + + codegen::generate(&lcx, &ir).unwrap_or_else(|err| { + dbg!(err); + std::process::exit(1); + }); }