From ee67ebb017835007f8060c0e37ec9891907cdf7f Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Sun, 29 Dec 2024 13:30:29 +0100 Subject: [PATCH] adding cranelift backend also splitting hbc binary into a separate crate to avoid dependency cycle, the backand is thus far capable of compiling the simplest program that returns an custom status code Signed-off-by: Jakub Doka --- .gitignore | 3 + Cargo.lock | 231 +++++++++++++- Cargo.toml | 2 + c/Cargo.toml | 10 + {lang => c}/src/main.rs | 20 +- cranelift-backend/Cargo.toml | 12 + cranelift-backend/src/lib.rs | 534 ++++++++++++++++++++++++++++++++ cranelift-backend/src/x86_64.rs | 299 ++++++++++++++++++ depell/wasm-hbc/src/lib.rs | 2 +- lang/Cargo.toml | 4 - lang/src/backend/hbvm.rs | 19 +- lang/src/fs.rs | 23 +- lang/src/lib.rs | 40 ++- lang/src/parser.rs | 5 +- lang/src/son.rs | 33 +- lang/src/ty.rs | 8 +- lang/src/utils.rs | 4 + smh.hb | 3 + 18 files changed, 1196 insertions(+), 56 deletions(-) create mode 100644 c/Cargo.toml rename {lang => c}/src/main.rs (61%) create mode 100644 cranelift-backend/Cargo.toml create mode 100644 cranelift-backend/src/lib.rs create mode 100644 cranelift-backend/src/x86_64.rs diff --git a/.gitignore b/.gitignore index 84b44e1..b5f8365 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ /target rustc-ice-* +a.out +out.o + # sqlite db.sqlite db.sqlite-journal diff --git a/Cargo.lock b/Cargo.lock index 29754c3..5855d91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,10 +39,22 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.89" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "arc-swap" @@ -229,7 +241,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn", "which", @@ -259,6 +271,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] + [[package]] name = "bytes" version = "1.8.0" @@ -340,6 +361,143 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-backend" +version = "0.1.0" +dependencies = [ + "cranelift-codegen", + "cranelift-frontend", + "cranelift-module", + "cranelift-object", + "hblang", + "target-lexicon", +] + +[[package]] +name = "cranelift-bforest" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-bitset" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a" + +[[package]] +name = "cranelift-codegen" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli 0.31.1", + "hashbrown 0.14.5", + "log", + "regalloc2", + "rustc-hash 2.1.0", + "serde", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181" + +[[package]] +name = "cranelift-control" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84" +dependencies = [ + "cranelift-bitset", +] + +[[package]] +name = "cranelift-frontend" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0" + +[[package]] +name = "cranelift-module" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11841b3f54ac480db1e8e8d5678ba901a13b387012d315e3f8fba3e7b7a80447" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", +] + +[[package]] +name = "cranelift-object" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e235ddfd19f100855ad03358c7ae0a13070c38a000701054cab46458cca6e81" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-module", + "log", + "object", + "target-lexicon", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -441,6 +599,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -532,6 +696,11 @@ name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator 0.3.0", + "indexmap 2.6.0", + "stable_deref_trait", +] [[package]] name = "glob" @@ -579,6 +748,9 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", +] [[package]] name = "hashlink" @@ -593,6 +765,16 @@ dependencies = [ name = "hbbytecode" version = "0.1.0" +[[package]] +name = "hbc" +version = "0.1.0" +dependencies = [ + "cranelift-backend", + "hblang", + "log", + "target-lexicon", +] + [[package]] name = "hblang" version = "0.1.0" @@ -935,6 +1117,9 @@ version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ + "crc32fast", + "hashbrown 0.15.0", + "indexmap 2.6.0", "memchr", ] @@ -1048,6 +1233,20 @@ dependencies = [ "getrandom", ] +[[package]] +name = "regalloc2" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.15.0", + "log", + "rustc-hash 2.1.0", + "smallvec", +] + [[package]] name = "regex" version = "1.11.1" @@ -1118,6 +1317,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustix" version = "0.38.37" @@ -1192,18 +1397,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -1295,9 +1500,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1316,6 +1521,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "time" version = "0.3.36" diff --git a/Cargo.toml b/Cargo.toml index 48734ae..09e09d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ members = [ "depell/wasm-fmt", "depell/wasm-hbc", "depell/wasm-rt", + "cranelift-backend", + "c", ] [workspace.dependencies] diff --git a/c/Cargo.toml b/c/Cargo.toml new file mode 100644 index 0000000..5fdf012 --- /dev/null +++ b/c/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hbc" +version = "0.1.0" +edition = "2024" + +[dependencies] +cranelift-backend = { version = "0.1.0", path = "../cranelift-backend" } +hblang = { workspace = true, features = ["std"] } +log = "0.4.22" +target-lexicon = { version = "0.12", features = ["std"] } diff --git a/lang/src/main.rs b/c/src/main.rs similarity index 61% rename from lang/src/main.rs rename to c/src/main.rs index dfc9103..17ffc38 100644 --- a/lang/src/main.rs +++ b/c/src/main.rs @@ -1,4 +1,5 @@ -#[cfg(feature = "std")] +use std::io; + fn main() { use std::io::Write; @@ -6,7 +7,22 @@ fn main() { let args = std::env::args().collect::>(); let args = args.iter().map(String::as_str).collect::>(); let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)]; - let opts = hblang::Options::from_args(&args, out, resolvers)?; + + let mut native = None; + let backend = match args.iter().position(|&v| v == "--target").map(|i| args[i + 1]) { + Some(hblang::backend::hbvm::TARGET_TRIPLE) => None, + Some(target) => Some( + native.insert( + cranelift_backend::Backend::new(target.parse().map_err(io::Error::other)?) + .map_err(io::Error::other)?, + ) as &mut dyn hblang::backend::Backend, + ), + None => Some(native.insert( + cranelift_backend::Backend::new(target_lexicon::HOST).map_err(io::Error::other)?, + ) as &mut dyn hblang::backend::Backend), + }; + + let opts = hblang::Options::from_args(&args, out, resolvers, backend)?; let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"); hblang::run_compiler(file, opts, out, warnings) diff --git a/cranelift-backend/Cargo.toml b/cranelift-backend/Cargo.toml new file mode 100644 index 0000000..660df2d --- /dev/null +++ b/cranelift-backend/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cranelift-backend" +version = "0.1.0" +edition = "2024" + +[dependencies] +cranelift-codegen = "0.115.0" +cranelift-frontend = "0.115.0" +cranelift-module = "0.115.0" +cranelift-object = "0.115.0" +hblang.workspace = true +target-lexicon = "0.12" diff --git a/cranelift-backend/src/lib.rs b/cranelift-backend/src/lib.rs new file mode 100644 index 0000000..6d98f5b --- /dev/null +++ b/cranelift-backend/src/lib.rs @@ -0,0 +1,534 @@ +#![feature(if_let_guard)] +#![feature(slice_take)] +use { + cranelift_codegen::{ + CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, + ir::{InstBuilder, UserExternalName}, + isa::LookupError, + settings::Configurable, + }, + cranelift_frontend::FunctionBuilder, + cranelift_module::{Module, ModuleError}, + hblang::{ + nodes::Kind, + utils::{Ent, EntVec}, + }, + std::{fmt::Display, ops::Range}, +}; + +mod x86_64; + +pub struct Backend { + ctx: cranelift_codegen::Context, + dt_ctx: cranelift_module::DataDescription, + fb_ctx: cranelift_frontend::FunctionBuilderContext, + module: Option, + ctrl_plane: cranelift_codegen::control::ControlPlane, + funcs: Functions, + globals: EntVec, + asm: Assembler, +} + +impl Backend { + pub fn new(triple: target_lexicon::Triple) -> Result { + Ok(Self { + ctx: cranelift_codegen::Context::new(), + dt_ctx: cranelift_module::DataDescription::new(), + fb_ctx: cranelift_frontend::FunctionBuilderContext::default(), + ctrl_plane: cranelift_codegen::control::ControlPlane::default(), + module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new( + cranelift_codegen::isa::lookup(triple)?.finish( + cranelift_codegen::settings::Flags::new({ + let mut bl = cranelift_codegen::settings::builder(); + bl.set("enable_verifier", "true").unwrap(); + bl + }), + )?, + "main", + cranelift_module::default_libcall_names(), + )?) + .into(), + funcs: Default::default(), + globals: Default::default(), + asm: Default::default(), + }) + } +} + +impl hblang::backend::Backend for Backend { + fn assemble_reachable( + &mut self, + from: hblang::ty::Func, + types: &hblang::ty::Types, + files: &hblang::utils::EntSlice, + to: &mut Vec, + ) -> hblang::backend::AssemblySpec { + debug_assert!(self.asm.frontier.is_empty()); + debug_assert!(self.asm.funcs.is_empty()); + debug_assert!(self.asm.globals.is_empty()); + + let mut module = self.module.take().expect("backend can assemble only once"); + + fn clif_name_to_ty(name: UserExternalName) -> hblang::ty::Id { + match name.namespace { + 0 => hblang::ty::Kind::Func(hblang::ty::Func::new(name.index as _)), + 1 => hblang::ty::Kind::Global(hblang::ty::Global::new(name.index as _)), + _ => unreachable!(), + } + .compress() + } + + self.globals.shadow(types.ins.globals.len()); + + self.asm.frontier.push(from.into()); + while let Some(itm) = self.asm.frontier.pop() { + match itm.expand() { + hblang::ty::Kind::Func(func) => { + let fuc = &mut self.funcs.headers[func]; + self.asm.funcs.push(func); + self.asm.frontier.extend( + fuc.external_names.clone().map(|r| { + clif_name_to_ty(self.funcs.external_names[r as usize].clone()) + }), + ); + self.asm.name.clear(); + if func == from { + self.asm.name.push_str("main"); + } else { + let file = &files[types.ins.funcs[func].file]; + self.asm.name.push_str(&file.path); + self.asm.name.push('.'); + self.asm.name.push_str(file.ident_str(types.ins.funcs[func].name)); + } + let linkage = if func == from { + cranelift_module::Linkage::Export + } else { + cranelift_module::Linkage::Local + }; + build_signature( + module.isa().default_call_conv(), + types.ins.funcs[func].sig, + types, + &mut self.ctx.func.signature, + &mut vec![], + ); + fuc.module_id = Some( + module + .declare_function(&self.asm.name, linkage, &self.ctx.func.signature) + .unwrap(), + ); + } + hblang::ty::Kind::Global(glob) => { + self.asm.globals.push(glob); + self.asm.name.clear(); + let file = &files[types.ins.globals[glob].file]; + self.asm.name.push_str(&file.path); + self.asm.name.push('.'); + self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name)); + self.globals[glob].module_id = Some( + module + .declare_data( + &self.asm.name, + cranelift_module::Linkage::Local, + true, + false, + ) + .unwrap(), + ); + } + _ => unreachable!(), + } + } + + for &func in &self.asm.funcs { + let fuc = &self.funcs.headers[func]; + debug_assert!(!fuc.code.is_empty()); + let names = &mut self.funcs.external_names + [fuc.external_names.start as usize..fuc.external_names.end as usize]; + names.iter_mut().for_each(|nm| { + nm.index = fuc.module_id.unwrap().as_u32(); + self.ctx.func.params.ensure_user_func_name(nm.clone()); + }); + module + .define_function_bytes( + fuc.module_id.unwrap(), + &self.ctx.func, + fuc.alignment as _, + &self.funcs.code[fuc.code.start as usize..fuc.code.end as usize], + &self.funcs.relocs[fuc.relocs.start as usize..fuc.relocs.end as usize], + ) + .unwrap(); + } + + for global in self.asm.globals.drain(..) { + let glob = &self.globals[global]; + self.dt_ctx.clear(); + self.dt_ctx.define(types.ins.globals[global].data.clone().into()); + module.define_data(glob.module_id.unwrap(), &self.dt_ctx).unwrap(); + } + + module.finish().object.write_stream(to).unwrap(); + + hblang::backend::AssemblySpec { code_length: 0, data_length: 0, entry: 0 } + } + + fn disasm<'a>( + &'a self, + _sluce: &[u8], + _eca_handler: &mut dyn FnMut(&mut &[u8]), + _types: &'a hblang::ty::Types, + _files: &'a hblang::utils::EntSlice, + _output: &mut String, + ) -> Result<(), std::boxed::Box> { + unimplemented!() + } + + fn emit_body( + &mut self, + id: hblang::ty::Func, + nodes: &hblang::nodes::Nodes, + tys: &hblang::ty::Types, + files: &hblang::utils::EntSlice, + ) { + self.ctx.clear(); + + let mut lens = vec![]; + let stack_ret = build_signature( + self.module.as_ref().unwrap().isa().default_call_conv(), + tys.ins.funcs[id].sig, + tys, + &mut self.ctx.func.signature, + &mut lens, + ); + + FuncBuilder { + bl: FunctionBuilder::new(&mut self.ctx.func, &mut self.fb_ctx), + nodes, + tys, + files, + values: &mut vec![None; nodes.len()], + } + .build(tys.ins.funcs[id].sig, &lens, stack_ret); + + self.ctx.compile(self.module.as_ref().unwrap().isa(), &mut self.ctrl_plane).unwrap(); + let code = self.ctx.compiled_code().unwrap(); + self.funcs.push(id, &self.ctx.func, &code.buffer); + } +} + +fn build_signature( + call_conv: cranelift_codegen::isa::CallConv, + sig: hblang::ty::Sig, + types: &hblang::ty::Types, + signature: &mut cranelift_codegen::ir::Signature, + arg_lens: &mut Vec, +) -> bool { + signature.clear(call_conv); + match call_conv { + cranelift_codegen::isa::CallConv::SystemV => { + x86_64::build_systemv_signature(sig, types, signature, arg_lens) + } + _ => todo!(), + } +} + +struct FuncBuilder<'a, 'b> { + bl: cranelift_frontend::FunctionBuilder<'b>, + nodes: &'a hblang::nodes::Nodes, + tys: &'a hblang::ty::Types, + #[expect(unused)] + files: &'a hblang::utils::EntSlice, + values: &'b mut [Option>], +} + +impl FuncBuilder<'_, '_> { + pub fn build(mut self, sig: hblang::ty::Sig, arg_lens: &[usize], stack_ret: bool) { + let entry = self.bl.create_block(); + self.bl.append_block_params_for_function_params(entry); + self.bl.switch_to_block(entry); + let mut arg_vals = self.bl.block_params(entry); + + if stack_ret { + let ret_ptr = *arg_vals.take_first().unwrap(); + self.values[hblang::nodes::MEM as usize] = Some(Ok(ret_ptr)); + } + + let Self { nodes, tys, .. } = self; + + let mut parama_len = arg_lens.iter(); + let mut typs = sig.args.args(); + let mut args = nodes[hblang::nodes::VOID].outputs[hblang::nodes::ARG_START..].iter(); + while let Some(aty) = typs.next(tys) { + let hblang::ty::Arg::Value(ty) = aty else { continue }; + let loc = arg_vals.take(..*parama_len.next().unwrap()).unwrap(); + let &arg = args.next().unwrap(); + if ty.is_aggregate(tys) { + todo!() + } else { + debug_assert_eq!(loc.len(), 0); + self.values[arg as usize] = Some(Ok(loc[0])); + } + } + + self.values[hblang::nodes::ENTRY as usize] = Some(Err(entry)); + + self.emit_node(hblang::nodes::VOID, hblang::nodes::VOID); + + self.bl.finalize(); + } + + fn value_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Value { + self.values[nid as usize].unwrap().unwrap() + } + + fn block_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Block { + self.values[nid as usize].unwrap().unwrap_err() + } + + fn close_block(&mut self, nid: hblang::nodes::Nid) { + if matches!(self.nodes[nid].kind, Kind::Loop | Kind::Region) { + return; + } + self.bl.seal_block(self.block_of(nid)); + } + + fn emit_node(&mut self, nid: hblang::nodes::Nid, block: hblang::nodes::Nid) { + use hblang::nodes::*; + + let mut args = vec![]; + if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { + let side = 1 + self.values[nid as usize].is_some() as usize; + for &o in self.nodes[nid].outputs.iter() { + if self.nodes[o].is_data_phi() { + args.push(self.value_of(self.nodes[0].inputs[side])); + } + } + match (self.nodes[nid].kind, self.values[nid as usize]) { + (Kind::Loop, Some(blck)) => { + self.bl.ins().jump(blck.unwrap_err(), &args); + self.bl.seal_block(blck.unwrap_err()); + return; + } + (Kind::Region, None) => { + let next = self.bl.create_block(); + for &o in self.nodes[nid].outputs.iter() { + if self.nodes[o].is_data_phi() { + self.values[o as usize] = Some(Ok(self.bl.append_block_param( + next, + ty_to_clif_ty(self.nodes[o].ty, self.tys), + ))); + } + } + self.bl.ins().jump(next, &args); + self.bl.seal_block(next); + self.values[nid as usize] = Some(Err(next)); + return; + } + _ => {} + } + } + + let node = &self.nodes[nid]; + self.values[nid as usize] = Some(match node.kind { + Kind::Start => { + debug_assert_eq!(self.nodes[node.outputs[0]].kind, Kind::Entry); + self.emit_node(node.outputs[0], block); + return; + } + Kind::If => { + let &[_, cnd] = node.inputs.as_slice() else { unreachable!() }; + let &[then, else_] = node.outputs.as_slice() else { unreachable!() }; + + let then_bl = self.bl.create_block(); + let else_bl = self.bl.create_block(); + let c = self.value_of(cnd); + self.bl.ins().brif(c, then_bl, &[], else_bl, &[]); + self.values[then as usize] = Some(Err(then_bl)); + self.values[else_ as usize] = Some(Err(else_bl)); + + self.close_block(block); + self.bl.switch_to_block(then_bl); + self.emit_node(then, then); + self.bl.switch_to_block(else_bl); + self.emit_node(else_, else_); + Err(self.block_of(block)) + } + Kind::Region | Kind::Loop => { + if node.kind == Kind::Loop { + let next = self.bl.create_block(); + for &o in self.nodes[nid].outputs.iter() { + if self.nodes[o].is_data_phi() { + self.values[o as usize] = Some(Ok(self.bl.append_block_param( + next, + ty_to_clif_ty(self.nodes[o].ty, self.tys), + ))); + } + } + self.values[nid as usize] = Some(Err(next)); + } + self.bl.ins().jump(self.values[nid as usize].unwrap().unwrap_err(), &args); + self.close_block(block); + self.bl.switch_to_block(self.values[nid as usize].unwrap().unwrap_err()); + for &o in node.outputs.iter().rev() { + self.emit_node(o, nid); + } + Err(self.block_of(block)) + } + Kind::Return { .. } | Kind::Die => { + let ret = self.value_of(node.inputs[1]); + self.bl.ins().return_(&[ret]); + self.close_block(block); + self.emit_node(node.outputs[0], block); + Err(self.block_of(block)) + } + Kind::Entry => { + for &o in node.outputs.iter().rev() { + self.emit_node(o, nid); + } + return; + } + Kind::Then | Kind::Else => { + for &o in node.outputs.iter().rev() { + self.emit_node(o, block); + } + Err(self.block_of(block)) + } + Kind::Call { func: _, unreachable, .. } => { + if unreachable { + todo!() + } else { + todo!(); + //for &o in node.outputs.iter().rev() { + // if self.nodes[o].inputs[0] == nid + // || (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region) + // && self.nodes[o].inputs[1] == nid) + // { + // self.emit_node(o, block); + // } + //} + } + } + Kind::CInt { value } if self.nodes[nid].ty.is_integer() => Ok(self.bl.ins().iconst( + cranelift_codegen::ir::Type::int(self.tys.size_of(self.nodes[nid].ty) as u16 * 8) + .unwrap(), + value, + )), + Kind::CInt { value } => Ok(match self.tys.size_of(self.nodes[nid].ty) { + 4 => self.bl.ins().f32const(f64::from_bits(value as _) as f32), + 8 => self.bl.ins().f64const(f64::from_bits(value as _)), + _ => unimplemented!(), + }), + Kind::BinOp { .. } + | Kind::UnOp { .. } + | Kind::Global { .. } + | Kind::Load { .. } + | Kind::Stre + | Kind::RetVal + | Kind::Stck => todo!(), + Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops | Kind::Join => return, + Kind::Assert { .. } => unreachable!(), + }); + } +} + +fn ty_to_clif_ty(ty: hblang::ty::Id, tys: &hblang::ty::Types) -> cranelift_codegen::ir::Type { + if ty.is_integer() { + cranelift_codegen::ir::Type::int(tys.size_of(ty) as u16 * 8).unwrap() + } else { + unimplemented!() + } +} + +#[derive(Default)] +struct Global { + module_id: Option, +} + +#[derive(Default)] +struct FuncHeaders { + module_id: Option, + alignment: u32, + code: Range, + relocs: Range, + external_names: Range, +} + +#[derive(Default)] +struct Functions { + headers: EntVec, + code: Vec, + relocs: Vec, + external_names: Vec, +} + +impl Functions { + fn push( + &mut self, + id: hblang::ty::Func, + func: &cranelift_codegen::ir::Function, + code: &MachBufferFinalized, + ) { + self.headers.shadow(id.index() + 1); + self.headers[id] = FuncHeaders { + module_id: None, + alignment: code.alignment, + code: self.code.len() as u32..self.code.len() as u32 + code.data().len() as u32, + relocs: self.relocs.len() as u32..self.relocs.len() as u32 + code.relocs().len() as u32, + external_names: self.external_names.len() as u32 + ..self.external_names.len() as u32 + func.params.user_named_funcs().len() as u32, + }; + self.code.extend(code.data()); + self.relocs.extend(code.relocs().iter().cloned()); + self.external_names.extend(func.params.user_named_funcs().values().cloned()); + } +} + +#[derive(Default)] +struct Assembler { + name: String, + frontier: Vec, + globals: Vec, + funcs: Vec, +} + +#[derive(Debug)] +pub enum BackendCreationError { + UnsupportedTriplet(LookupError), + InvalidFlags(CodegenError), + UnsupportedModuleConfig(ModuleError), +} + +impl Display for BackendCreationError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BackendCreationError::UnsupportedTriplet(err) => { + write!(f, "Unsupported triplet: {}", err) + } + BackendCreationError::InvalidFlags(err) => { + write!(f, "Invalid flags: {}", err) + } + BackendCreationError::UnsupportedModuleConfig(err) => { + write!(f, "Unsupported module configuration: {}", err) + } + } + } +} +impl core::error::Error for BackendCreationError {} + +impl From for BackendCreationError { + fn from(value: LookupError) -> Self { + Self::UnsupportedTriplet(value) + } +} + +impl From for BackendCreationError { + fn from(value: CodegenError) -> Self { + Self::InvalidFlags(value) + } +} + +impl From for BackendCreationError { + fn from(value: ModuleError) -> Self { + Self::UnsupportedModuleConfig(value) + } +} diff --git a/cranelift-backend/src/x86_64.rs b/cranelift-backend/src/x86_64.rs new file mode 100644 index 0000000..851d9fb --- /dev/null +++ b/cranelift-backend/src/x86_64.rs @@ -0,0 +1,299 @@ +// The classification code for the x86_64 ABI is taken from the clay language +// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp + +pub fn build_systemv_signature( + sig: hblang::ty::Sig, + types: &hblang::ty::Types, + signature: &mut cranelift_codegen::ir::Signature, + arg_lens: &mut Vec, +) -> bool { + let mut alloca = Alloca::new(); + + alloca.next(false, sig.ret, types, &mut signature.returns); + let stack_ret = signature.params.len() == 1 + && signature.params[0].purpose == cranelift_codegen::ir::ArgumentPurpose::StructReturn; + + if stack_ret { + signature.params.append(&mut signature.returns); + } + + let mut args = sig.args.args(); + while let Some(arg) = args.next_value(types) { + let prev = signature.params.len(); + alloca.next(true, arg, types, &mut signature.params); + arg_lens.push(signature.params.len() - prev); + } + + stack_ret +} + +/// Classification of "eightbyte" components. +// N.B., the order of the variants is from general to specific, +// such that `unify(a, b)` is the "smaller" of `a` and `b`. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +enum Class { + Int, + Sse, + SseUp, +} + +#[derive(Clone, Copy, Debug)] +struct Memory; + +// Currently supported vector size (AVX-512). +const LARGEST_VECTOR_SIZE: usize = 512; +const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64; + +fn classify_arg( + cx: &hblang::ty::Types, + arg: hblang::ty::Id, +) -> Result<[Option; MAX_EIGHTBYTES], Memory> { + fn classify( + cx: &hblang::ty::Types, + layout: hblang::ty::Id, + cls: &mut [Option], + off: hblang::ty::Offset, + ) -> Result<(), Memory> { + let size = cx.size_of(layout); + if off & (cx.align_of(layout) - 1) != 0 { + if size != 0 { + return Err(Memory); + } + return Ok(()); + } + + let mut c = match layout.expand() { + _ if size == 0 => return Ok(()), + _ if layout.is_integer() || layout.is_pointer() => Class::Int, + _ if layout.is_float() => Class::Sse, + + hblang::ty::Kind::Struct(s) => { + for (f, foff) in hblang::ty::OffsetIter::new(s, cx).into_iter(cx) { + classify(cx, f.ty, cls, off + foff)?; + } + return Ok(()); + } + hblang::ty::Kind::Tuple(tuple) => { + for (&ty, foff) in hblang::ty::OffsetIter::new(tuple, cx).into_iter(cx) { + classify(cx, ty, cls, off + foff)?; + } + return Ok(()); + } + hblang::ty::Kind::Enum(_) => Class::Int, + hblang::ty::Kind::Union(union) => { + for f in cx.union_fields(union) { + classify(cx, f.ty, cls, off)?; + } + return Ok(()); + } + hblang::ty::Kind::Slice(slice) if let Some(len) = cx.ins.slices[slice].len() => { + for i in 0..len as u32 { + classify( + cx, + cx.ins.slices[slice].elem, + cls, + off + i * cx.size_of(cx.ins.slices[slice].elem), + )?; + } + return Ok(()); + } + hblang::ty::Kind::Slice(_) => { + classify(cx, hblang::ty::Id::UINT, cls, off)?; + classify(cx, hblang::ty::Id::UINT, cls, off + 8)?; + return Ok(()); + } + hblang::ty::Kind::Opt(opt) => { + let base = cx.ins.opts[opt].base; + if cx.nieche_of(base).is_some() { + classify(cx, base, cls, off)?; + } else { + classify(cx, hblang::ty::Id::BOOL, cls, off)?; + classify(cx, base, cls, off + cx.align_of(base))?; + } + return Ok(()); + } + + _ => unimplemented!(), + }; + + // Fill in `cls` for scalars (Int/Sse) and vectors (Sse). + let first = (off / 8) as usize; + let last = ((off + size - 1) / 8) as usize; + for cls in &mut cls[first..=last] { + *cls = Some(cls.map_or(c, |old| old.min(c))); + + // Everything after the first Sse "eightbyte" + // component is the upper half of a register. + if c == Class::Sse { + c = Class::SseUp; + } + } + + Ok(()) + } + + let size = cx.size_of(arg); + let n = ((size + 7) / 8) as usize; + if n > MAX_EIGHTBYTES { + return Err(Memory); + } + + let mut cls = [None; MAX_EIGHTBYTES]; + classify(cx, arg, &mut cls, 0)?; + if n > 2 { + if cls[0] != Some(Class::Sse) { + return Err(Memory); + } + if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) { + return Err(Memory); + } + } else { + let mut i = 0; + while i < n { + if cls[i] == Some(Class::SseUp) { + cls[i] = Some(Class::Sse); + } else if cls[i] == Some(Class::Sse) { + i += 1; + while i != n && cls[i] == Some(Class::SseUp) { + i += 1; + } + } else { + i += 1; + } + } + } + + Ok(cls) +} + +fn reg_component( + cls: &[Option], + i: &mut usize, + size: hblang::ty::Size, +) -> Option { + if *i >= cls.len() { + return None; + } + + match cls[*i] { + None => None, + Some(Class::Int) => { + *i += 1; + Some(if size < 8 { + cranelift_codegen::ir::Type::int(size as u16 * 8).unwrap() + } else { + cranelift_codegen::ir::types::I64 + }) + } + Some(Class::Sse) => { + let vec_len = + 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count(); + *i += vec_len; + Some(if vec_len == 1 { + match size { + 4 => cranelift_codegen::ir::types::F32, + _ => cranelift_codegen::ir::types::F64, + } + } else { + cranelift_codegen::ir::types::I64.by(vec_len as _).unwrap() + }) + } + Some(c) => unreachable!("reg_component: unhandled class {:?}", c), + } +} + +fn cast_target( + cls: &[Option], + size: hblang::ty::Size, + dest: &mut Vec, +) { + let mut i = 0; + let lo = reg_component(cls, &mut i, size).unwrap(); + let offset = 8 * (i as u32); + dest.push(cranelift_codegen::ir::AbiParam::new(lo)); + if size > offset { + if let Some(hi) = reg_component(cls, &mut i, size - offset) { + dest.push(cranelift_codegen::ir::AbiParam::new(hi)); + } + } + assert_eq!(reg_component(cls, &mut i, 0), None); +} + +const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9 +const MAX_SSE_REGS: usize = 8; // XMM0-7 + +pub struct Alloca { + int_regs: usize, + sse_regs: usize, +} + +impl Alloca { + pub fn new() -> Self { + Self { int_regs: MAX_INT_REGS, sse_regs: MAX_SSE_REGS } + } + + pub fn next( + &mut self, + is_arg: bool, + arg: hblang::ty::Id, + cx: &hblang::ty::Types, + dest: &mut Vec, + ) { + let mut cls_or_mem = classify_arg(cx, arg); + + if is_arg { + if let Ok(cls) = cls_or_mem { + let mut needed_int = 0; + let mut needed_sse = 0; + for c in cls { + match c { + Some(Class::Int) => needed_int += 1, + Some(Class::Sse) => needed_sse += 1, + _ => {} + } + } + match (self.int_regs.checked_sub(needed_int), self.sse_regs.checked_sub(needed_sse)) + { + (Some(left_int), Some(left_sse)) => { + self.int_regs = left_int; + self.sse_regs = left_sse; + } + _ => { + // Not enough registers for this argument, so it will be + // passed on the stack, but we only mark aggregates + // explicitly as indirect `byval` arguments, as LLVM will + // automatically put immediates on the stack itself. + if arg.is_aggregate(cx) { + cls_or_mem = Err(Memory); + } + } + } + } + } + + match cls_or_mem { + Err(Memory) => { + if is_arg { + dest.push(cranelift_codegen::ir::AbiParam::new( + cranelift_codegen::ir::types::I64, + )); + } else { + dest.push(cranelift_codegen::ir::AbiParam::special( + cranelift_codegen::ir::types::I64, + cranelift_codegen::ir::ArgumentPurpose::StructReturn, + )); + } + } + Ok(ref cls) => { + // split into sized chunks passed individually + if arg.is_aggregate(cx) { + cast_target(cls, cx.size_of(arg), dest); + } else { + dest.push(cranelift_codegen::ir::AbiParam::new( + reg_component(cls, &mut 0, cx.size_of(arg)).unwrap(), + )); + } + } + } + } +} diff --git a/depell/wasm-hbc/src/lib.rs b/depell/wasm-hbc/src/lib.rs index c34f2ad..1c7316b 100644 --- a/depell/wasm-hbc/src/lib.rs +++ b/depell/wasm-hbc/src/lib.rs @@ -9,7 +9,7 @@ use { backend::hbvm::HbvmBackend, son::{Codegen, CodegenCtx}, ty::Module, - Ent, + utils::Ent, }, }; diff --git a/lang/Cargo.toml b/lang/Cargo.toml index effdee2..8ce808f 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -3,10 +3,6 @@ name = "hblang" version = "0.1.0" edition = "2021" -[[bin]] -name = "hbc" -path = "src/main.rs" - [[bin]] name = "fuzz" path = "src/fuzz_main.rs" diff --git a/lang/src/backend/hbvm.rs b/lang/src/backend/hbvm.rs index af18293..f3a6380 100644 --- a/lang/src/backend/hbvm.rs +++ b/lang/src/backend/hbvm.rs @@ -8,7 +8,7 @@ use { utils::{EntSlice, EntVec}, }, alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}, - core::{assert_matches::debug_assert_matches, mem, ops::Range}, + core::{assert_matches::debug_assert_matches, error, mem, ops::Range}, hbbytecode::{self as instrs, *}, reg::Reg, }; @@ -106,6 +106,8 @@ pub struct HbvmBackend { offsets: Vec, } +pub const TARGET_TRIPLE: &str = "hbvm-virt-ableos"; + impl HbvmBackend { fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { emit(&mut self.code, instr); @@ -113,12 +115,18 @@ impl HbvmBackend { } impl Backend for HbvmBackend { - fn assemble_bin(&mut self, entry: ty::Func, types: &Types, to: &mut Vec) { + fn assemble_bin( + &mut self, + entry: ty::Func, + types: &Types, + files: &EntSlice, + to: &mut Vec, + ) { to.extend([0u8; HEADER_SIZE]); binary_prelude(to); let AssemblySpec { code_length, data_length, entry } = - self.assemble_reachable(entry, types, to); + self.assemble_reachable(entry, types, files, to); let exe = AbleOsExecutableHeader { magic_number: [0x15, 0x91, 0xD2], @@ -138,6 +146,7 @@ impl Backend for HbvmBackend { &mut self, from: ty::Func, types: &Types, + _files: &EntSlice, to: &mut Vec, ) -> AssemblySpec { debug_assert!(self.asm.frontier.is_empty()); @@ -215,7 +224,7 @@ impl Backend for HbvmBackend { types: &'a Types, files: &'a EntSlice, output: &mut String, - ) -> Result<(), hbbytecode::DisasmError<'a>> { + ) -> Result<(), alloc::boxed::Box> { use hbbytecode::DisasmItem; let functions = types .ins @@ -250,7 +259,7 @@ impl Backend for HbvmBackend { }), ) .collect::>(); - hbbytecode::disasm(&mut sluce, &functions, output, eca_handler) + hbbytecode::disasm(&mut sluce, &functions, output, eca_handler).map_err(Into::into) } fn emit_ct_body( diff --git a/lang/src/fs.rs b/lang/src/fs.rs index 61a8f46..8134860 100644 --- a/lang/src/fs.rs +++ b/lang/src/fs.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::hbvm::HbvmBackend, + backend::{hbvm::HbvmBackend, Backend}, parser::{Ast, Ctx, FileKind}, son::{self}, ty, FnvBuildHasher, @@ -12,7 +12,6 @@ use { borrow::ToOwned, collections::VecDeque, eprintln, - ffi::OsStr, io::{self, Write as _}, path::{Path, PathBuf}, string::ToString, @@ -72,6 +71,7 @@ pub struct Options<'a> { pub dump_asm: bool, pub extra_threads: usize, pub resolver: Option>, + pub backend: Option<&'a mut dyn Backend>, } impl<'a> Options<'a> { @@ -79,6 +79,7 @@ impl<'a> Options<'a> { args: &[&str], out: &mut Vec, resolvers: &'a [(&str, PathResolver)], + backend: Option<&'a mut dyn Backend>, ) -> std::io::Result { if args.contains(&"--help") || args.contains(&"-h") { writeln!(out, "Usage: hbc [OPTIONS...] ")?; @@ -124,6 +125,7 @@ impl<'a> Options<'a> { ) }) .transpose()?, + backend, }) } } @@ -158,10 +160,11 @@ pub fn run_compiler( write!(out, "{}", &parsed.ast[0])?; } else { let mut backend = HbvmBackend::default(); + let backend = options.backend.unwrap_or(&mut backend); let mut ctx = crate::son::CodegenCtx::default(); *ctx.parser.errors.get_mut() = parsed.errors; - let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx); + let mut codegen = son::Codegen::new(backend, &parsed.ast, &mut ctx); codegen.push_embeds(parsed.embeds); codegen.generate(ty::Module::MAIN); @@ -299,7 +302,7 @@ pub struct CantLoadFile { impl core::fmt::Display for CantLoadFile { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "can't load file: {}", display_rel_path(&self.path),) + write!(f, "can't load file: {}", crate::display_rel_path(&self.path),) } } @@ -350,7 +353,7 @@ pub fn parse_from_fs( if !physiscal_path.exists() { return Err(io::Error::new( io::ErrorKind::NotFound, - format!("can't find file: {}", display_rel_path(&physiscal_path)), + format!("can't find file: {}", crate::display_rel_path(&physiscal_path)), )); } @@ -377,7 +380,7 @@ pub fn parse_from_fs( e.kind(), format!( "can't load embed file: {}: {e}", - display_rel_path(&physiscal_path) + crate::display_rel_path(&physiscal_path) ), ) })?; @@ -395,7 +398,7 @@ pub fn parse_from_fs( let path = path.to_str().ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidData, - format!("path contains invalid characters: {}", display_rel_path(&path)), + format!("path contains invalid characters: {}", crate::display_rel_path(&path)), ) })?; Ok(Ast::new(path, std::fs::read_to_string(path)?, ctx, &mut |path, from, kind| { @@ -441,9 +444,3 @@ pub fn parse_from_fs( errors, }) } - -pub fn display_rel_path(path: &(impl AsRef + ?Sized)) -> std::path::Display { - static CWD: std::sync::LazyLock = - std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default()); - std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display() -} diff --git a/lang/src/lib.rs b/lang/src/lib.rs index e93bde2..3e8d328 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -33,8 +33,10 @@ #[cfg(feature = "std")] pub use fs::*; -pub use utils::Ent; -use {self::ty::Builtin, alloc::vec::Vec}; +use { + self::{ty::Builtin, utils::Ent}, + alloc::vec::Vec, +}; #[macro_use] extern crate alloc; @@ -72,6 +74,7 @@ pub mod backend { utils::EntSlice, }, alloc::{string::String, vec::Vec}, + core::error, }; pub mod hbvm; @@ -87,6 +90,7 @@ pub mod backend { &mut self, from: ty::Func, types: &Types, + files: &EntSlice, to: &mut Vec, ) -> AssemblySpec; fn disasm<'a>( @@ -96,11 +100,11 @@ pub mod backend { types: &'a Types, files: &'a EntSlice, output: &mut String, - ) -> Result<(), hbbytecode::DisasmError<'a>>; + ) -> Result<(), alloc::boxed::Box>; fn emit_body( &mut self, id: ty::Func, - ci: &Nodes, + nodes: &Nodes, tys: &Types, files: &EntSlice, ); @@ -108,20 +112,26 @@ pub mod backend { fn emit_ct_body( &mut self, id: ty::Func, - ci: &Nodes, + nodes: &Nodes, tys: &Types, files: &EntSlice, ) { - self.emit_body(id, ci, tys, files); + self.emit_body(id, nodes, tys, files); } - fn assemble_bin(&mut self, from: ty::Func, types: &Types, to: &mut Vec) { - self.assemble_reachable(from, types, to); + fn assemble_bin( + &mut self, + from: ty::Func, + types: &Types, + files: &EntSlice, + to: &mut Vec, + ) { + self.assemble_reachable(from, types, files, to); } } } -mod utils; +pub mod utils; mod debug { use core::fmt::Debug; @@ -521,3 +531,15 @@ fn test_parse_files( embed_map.iter().map(|&(_, content)| content.to_owned().into_bytes()).collect(), ) } + +#[cfg(feature = "std")] +pub fn display_rel_path(path: &(impl AsRef + ?Sized)) -> std::path::Display { + static CWD: std::sync::LazyLock = + std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default()); + std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display() +} + +#[cfg(not(feature = "std"))] +pub fn display_rel_path(path: &str) -> &str { + path +} diff --git a/lang/src/parser.rs b/lang/src/parser.rs index 5035939..a105c6a 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -1461,10 +1461,7 @@ impl core::fmt::Display for Report<'_, D> { fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut impl fmt::Write) { let (line, mut col) = lexer::line_col(file.as_bytes(), pos); - #[cfg(feature = "std")] - let disp = crate::fs::display_rel_path(path); - #[cfg(not(feature = "std"))] - let disp = path; + let disp = crate::display_rel_path(path); _ = writeln!(out, "{}:{}:{}: {}", disp, line, col, msg); let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1) diff --git a/lang/src/son.rs b/lang/src/son.rs index b6e5bf3..e5e9563 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -27,10 +27,10 @@ use { core::{ assert_matches::debug_assert_matches, cell::RefCell, + error, fmt::{self, Debug, Display, Write}, format_args as fa, mem, }, - hbbytecode::DisasmError, }; const DEFAULT_ACLASS: usize = 0; @@ -616,16 +616,20 @@ impl<'a> Codegen<'a> { pub fn assemble_comptime(&mut self) -> Comptime { self.ct.code.clear(); - self.backend.assemble_bin(ty::Func::MAIN, self.tys, &mut self.ct.code); + self.backend.assemble_bin(ty::Func::MAIN, self.tys, self.files, &mut self.ct.code); self.ct.reset(); core::mem::take(self.ct) } pub fn assemble(&mut self, buf: &mut Vec) { - self.backend.assemble_bin(ty::Func::MAIN, self.tys, buf); + self.backend.assemble_bin(ty::Func::MAIN, self.tys, self.files, buf); } - pub fn disasm(&mut self, output: &mut String, bin: &[u8]) -> Result<(), DisasmError> { + pub fn disasm( + &mut self, + output: &mut String, + bin: &[u8], + ) -> Result<(), alloc::boxed::Box> { self.backend.disasm(bin, &mut |_| {}, self.tys, self.files, output) } @@ -669,7 +673,8 @@ impl<'a> Codegen<'a> { // TODO: return them back - let entry = self.ct_backend.assemble_reachable(fuc, self.tys, &mut self.ct.code).entry; + let entry = + self.ct_backend.assemble_reachable(fuc, self.tys, self.files, &mut self.ct.code).entry; #[cfg(debug_assertions)] { @@ -4403,8 +4408,24 @@ mod tests { core::fmt::Write, }; + pub struct Logger; + + impl log::Log for Logger { + fn enabled(&self, _: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + if self.enabled(record.metadata()) { + std::eprintln!("{}", record.args()) + } + } + + fn flush(&self) {} + } + fn generate(ident: &str, input: &str, output: &mut String) { - _ = log::set_logger(&crate::fs::Logger); + _ = log::set_logger(&Logger); log::set_max_level(log::LevelFilter::Info); //log::set_max_level(log::LevelFilter::Trace); diff --git a/lang/src/ty.rs b/lang/src/ty.rs index d919d42..d8b621b 100644 --- a/lang/src/ty.rs +++ b/lang/src/ty.rs @@ -83,7 +83,7 @@ pub enum Arg { } impl ArgIter { - pub(crate) fn next(&mut self, tys: &Types) -> Option { + pub fn next(&mut self, tys: &Types) -> Option { let ty = tys.ins.args[self.0.next()?]; if ty == Id::TYPE { return Some(Arg::Type(tys.ins.args[self.0.next().unwrap()])); @@ -91,7 +91,7 @@ impl ArgIter { Some(Arg::Value(ty)) } - pub(crate) fn next_value(&mut self, tys: &Types) -> Option { + pub fn next_value(&mut self, tys: &Types) -> Option { loop { match self.next(tys)? { Arg::Type(_) => continue, @@ -299,6 +299,10 @@ impl Id { _ => false, } } + + pub fn is_aggregate(&self, tys: &Types) -> bool { + self.loc(tys) == Loc::Stack + } } #[derive(PartialEq, Eq, Clone, Copy)] diff --git a/lang/src/utils.rs b/lang/src/utils.rs index 2fce52a..0a9bdb4 100644 --- a/lang/src/utils.rs +++ b/lang/src/utils.rs @@ -363,6 +363,10 @@ impl Vc { } } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + fn len_mut(&mut self) -> &mut Nid { unsafe { if self.is_inline() { diff --git a/smh.hb b/smh.hb index e69de29..935e4eb 100644 --- a/smh.hb +++ b/smh.hb @@ -0,0 +1,3 @@ +main := fn(): uint { + return 69 +}