diff --git a/.gitignore b/.gitignore index b5f8365..818cf4a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ rustc-ice-* a.out out.o +/examples/raylib/main # sqlite db.sqlite diff --git a/Cargo.lock b/Cargo.lock index 5855d91..4ee939b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,55 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.95" @@ -323,6 +372,46 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "cmake" version = "0.1.51" @@ -332,6 +421,12 @@ dependencies = [ "cc", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "const_format" version = "0.2.33" @@ -572,7 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -769,6 +864,7 @@ version = "0.1.0" name = "hbc" version = "0.1.0" dependencies = [ + "clap", "cranelift-backend", "hblang", "log", @@ -818,7 +914,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -945,6 +1041,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.12.1" @@ -1086,7 +1188,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1288,7 +1390,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1333,7 +1435,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1477,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1492,6 +1594,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -1559,7 +1667,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1690,6 +1798,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1805,6 +1919,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/c/Cargo.toml b/c/Cargo.toml index 5fdf012..336601c 100644 --- a/c/Cargo.toml +++ b/c/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +clap = { version = "4.5.23", features = ["derive", "env"] } cranelift-backend = { version = "0.1.0", path = "../cranelift-backend" } hblang = { workspace = true, features = ["std"] } log = "0.4.22" diff --git a/c/src/main.rs b/c/src/main.rs index 17ffc38..7dac7ca 100644 --- a/c/src/main.rs +++ b/c/src/main.rs @@ -1,31 +1,78 @@ -use std::io; +use { + clap::Parser, + std::{io, str::FromStr}, +}; + +#[derive(Parser)] +struct Args { + /// format depends on the backend used + /// - cranelift-backend expects `=,...` pass `help=me` to see options + #[clap(long, env, default_value = "")] + backend_flags: String, + #[clap(long, short, env, default_value_t = target_lexicon::HOST)] + target: target_lexicon::Triple, + #[clap(long, env, value_parser = ["ableos"])] + path_resolver: Option, + /// format the source code reachable form the root file + #[clap(long, env, default_value_t = false, conflicts_with_all = &["fmt_stdout", "dump_asm"])] + fmt: bool, + /// format the root file only and output the formatted file into stdout + #[clap(long, env, default_value_t = false, conflicts_with_all = &["fmt", "dump_asm"])] + fmt_stdout: bool, + #[clap(long, env, default_value_t = false, conflicts_with_all = &["fmt", "fmt_stdout"])] + dump_asm: bool, + /// extra threads to be used during compilation (currently only parser is parallelized) + #[clap(long, env, default_value_t = 0)] + extra_threads: usize, + /// path to the root file + file: String, +} fn main() { use std::io::Write; fn run(out: &mut Vec, warnings: &mut String) -> std::io::Result<()> { - let args = std::env::args().collect::>(); - let args = args.iter().map(String::as_str).collect::>(); + let Args { + backend_flags, + target, + path_resolver, + fmt, + fmt_stdout, + dump_asm, + extra_threads, + file, + } = Args::parse(); + let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)]; 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( + let backend = if target + == target_lexicon::Triple::from_str(hblang::backend::hbvm::TARGET_TRIPLE).unwrap() + { + None + } else { + Some( native.insert( - cranelift_backend::Backend::new(target.parse().map_err(io::Error::other)?) + cranelift_backend::Backend::new(target, &backend_flags) .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"); + let opts = hblang::Options { + fmt, + fmt_stdout, + dump_asm, + extra_threads, + resolver: resolvers + .iter() + .copied() + .find(|&(name, _)| Some(name) == path_resolver.as_deref()) + .map(|(_, v)| v), + backend, + }; - hblang::run_compiler(file, opts, out, warnings) + hblang::run_compiler(&file, opts, out, warnings) } log::set_logger(&hblang::fs::Logger).unwrap(); @@ -38,9 +85,10 @@ fn main() { std::io::stderr().write_all(warnings.as_bytes()).unwrap(); std::io::stdout().write_all(&out).unwrap() } - Err(_) => { + Err(e) => { std::io::stderr().write_all(warnings.as_bytes()).unwrap(); std::io::stderr().write_all(&out).unwrap(); + std::eprint!("{e}"); std::process::exit(1); } } diff --git a/cranelift-backend/src/lib.rs b/cranelift-backend/src/lib.rs index 63b63b4..bf4d052 100644 --- a/cranelift-backend/src/lib.rs +++ b/cranelift-backend/src/lib.rs @@ -3,51 +3,59 @@ use { core::panic, cranelift_codegen::{ - CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, - ir::{InstBuilder, MemFlags, TrapCode, UserExternalName}, + self as cc, CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, + ir::{self as cir, InstBuilder, MemFlags, TrapCode, UserExternalName, condcodes}, isa::{LookupError, TargetIsa}, - settings::Configurable, + settings::{Configurable, SetError}, }, - cranelift_frontend::FunctionBuilder, - cranelift_module::{Module, ModuleError}, + cranelift_frontend::{self as cf, FunctionBuilder}, + cranelift_module::{self as cm, Module, ModuleError}, hblang::{ lexer::TokenKind, - nodes::Kind, - utils::{Ent, EntVec}, + nodes::{self as hbnodes}, + ty as hbty, + utils::{self as hbutils, Ent, EntVec}, + }, + std::{ + fmt::{Display, Write}, + ops::Range, }, - 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, + ctx: cc::Context, + dt_ctx: cm::DataDescription, + fb_ctx: cf::FunctionBuilderContext, module: Option, - ctrl_plane: cranelift_codegen::control::ControlPlane, + ctrl_plane: cc::control::ControlPlane, funcs: Functions, - globals: EntVec, + globals: EntVec, asm: Assembler, } impl Backend { - pub fn new(triple: target_lexicon::Triple) -> Result { + pub fn new(triple: target_lexicon::Triple, flags: &str) -> 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(), + ctx: cc::Context::new(), + dt_ctx: cm::DataDescription::new(), + fb_ctx: cf::FunctionBuilderContext::default(), + ctrl_plane: cc::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 - }), - )?, + cc::isa::lookup(triple)?.finish(cc::settings::Flags::new({ + let mut bl = cc::settings::builder(); + for (k, v) in flags.split(',').filter_map(|s| s.split_once('=')) { + bl.set(k, v).map_err(|err| BackendCreationError::InvalidFlag { + key: k.to_owned(), + value: v.to_owned(), + err, + })?; + } + bl + }))?, "main", - cranelift_module::default_libcall_names(), + cm::default_libcall_names(), )?) .into(), funcs: Default::default(), @@ -60,9 +68,9 @@ impl Backend { impl hblang::backend::Backend for Backend { fn assemble_reachable( &mut self, - from: hblang::ty::Func, - types: &hblang::ty::Types, - files: &hblang::utils::EntSlice, + from: hbty::Func, + types: &hbty::Types, + files: &hbutils::EntSlice, to: &mut Vec, ) -> hblang::backend::AssemblySpec { debug_assert!(self.asm.frontier.is_empty()); @@ -71,10 +79,10 @@ impl hblang::backend::Backend for Backend { let mut module = self.module.take().expect("backend can assemble only once"); - fn clif_name_to_ty(name: UserExternalName) -> hblang::ty::Id { + fn clif_name_to_ty(name: UserExternalName) -> hbty::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 _)), + 0 => hbty::Kind::Func(hbty::Func::new(name.index as _)), + 1 => hbty::Kind::Global(hbty::Global::new(name.index as _)), _ => unreachable!(), } .compress() @@ -85,12 +93,16 @@ impl hblang::backend::Backend for Backend { self.asm.frontier.push(from.into()); while let Some(itm) = self.asm.frontier.pop() { match itm.expand() { - hblang::ty::Kind::Func(func) => { + hbty::Kind::Func(func) => { + let fd = &types.ins.funcs[func]; + if fd.is_import { + self.funcs.headers.shadow(func.index() + 1); + } let fuc = &mut self.funcs.headers[func]; + let file = &files[fd.file]; if fuc.module_id.is_some() { continue; } - 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()) @@ -99,20 +111,23 @@ impl hblang::backend::Backend for Backend { self.asm.name.clear(); if func == from { self.asm.name.push_str("main"); + } else if fd.is_import { + self.asm.name.push_str(file.ident_str(fd.name)); } else { - let file = &files[types.ins.funcs[func].file]; self.asm.name.push_str(hblang::strip_cwd(&file.path)); self.asm.name.push('.'); - self.asm.name.push_str(file.ident_str(types.ins.funcs[func].name)); + self.asm.name.push_str(file.ident_str(fd.name)); } let linkage = if func == from { - cranelift_module::Linkage::Export + cm::Linkage::Export + } else if fd.is_import { + cm::Linkage::Import } else { - cranelift_module::Linkage::Local + cm::Linkage::Local }; build_signature( module.isa().default_call_conv(), - types.ins.funcs[func].sig, + fd.sig, types, &mut self.ctx.func.signature, &mut vec![], @@ -122,25 +137,31 @@ impl hblang::backend::Backend for Backend { .declare_function(&self.asm.name, linkage, &self.ctx.func.signature) .unwrap(), ); + if !fd.is_import { + self.asm.funcs.push(func); + } } - hblang::ty::Kind::Global(glob) => { + hbty::Kind::Global(glob) => { if self.globals[glob].module_id.is_some() { continue; } self.asm.globals.push(glob); + self.asm.name.clear(); - let file = &files[types.ins.globals[glob].file]; - self.asm.name.push_str(hblang::strip_cwd(&file.path)); - self.asm.name.push('.'); - self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name)); + let mutable = if types.ins.globals[glob].file == Default::default() { + writeln!(self.asm.name, "anon{}", glob.index()).unwrap(); + false + } else { + let file = &files[types.ins.globals[glob].file]; + self.asm.name.push_str(hblang::strip_cwd(&file.path)); + self.asm.name.push('.'); + self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name)); + true + }; + self.globals[glob].module_id = Some( module - .declare_data( - &self.asm.name, - cranelift_module::Linkage::Local, - true, - false, - ) + .declare_data(&self.asm.name, cm::Linkage::Local, mutable, false) .unwrap(), ); } @@ -150,14 +171,22 @@ impl hblang::backend::Backend for Backend { for &func in &self.asm.funcs { let fuc = &self.funcs.headers[func]; + assert!(!types.ins.funcs[func].is_import); 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 = self.funcs.headers[hblang::ty::Func::new(nm.index as _)] - .module_id - .unwrap() - .as_u32(); + self.ctx.func.clear(); + names.iter().for_each(|nm| { + let mut nm = nm.clone(); + if nm.namespace == 0 { + nm.index = self.funcs.headers[hbty::Func::new(nm.index as _)] + .module_id + .unwrap() + .as_u32(); + } else { + nm.index = + self.globals[hbty::Global::new(nm.index as _)].module_id.unwrap().as_u32(); + } self.ctx.func.params.ensure_user_func_name(nm.clone()); }); module @@ -187,8 +216,8 @@ impl hblang::backend::Backend for Backend { &'a self, _sluce: &[u8], _eca_handler: &mut dyn FnMut(&mut &[u8]), - _types: &'a hblang::ty::Types, - _files: &'a hblang::utils::EntSlice, + _types: &'a hbty::Types, + _files: &'a hbutils::EntSlice, _output: &mut String, ) -> Result<(), std::boxed::Box> { unimplemented!() @@ -196,10 +225,10 @@ impl hblang::backend::Backend for Backend { fn emit_body( &mut self, - id: hblang::ty::Func, - nodes: &hblang::nodes::Nodes, - tys: &hblang::ty::Types, - files: &hblang::utils::EntSlice, + id: hbty::Func, + nodes: &hbnodes::Nodes, + tys: &hbty::Types, + files: &hbutils::EntSlice, ) { self.ctx.clear(); let isa = self.module.as_ref().unwrap().isa(); @@ -226,12 +255,9 @@ impl hblang::backend::Backend for Backend { .build(tys.ins.funcs[id].sig); self.ctx.func.name = - cranelift_codegen::ir::UserFuncName::User(cranelift_codegen::ir::UserExternalName { - namespace: 0, - index: id.index() as _, - }); + cir::UserFuncName::User(cir::UserExternalName { namespace: 0, index: id.index() as _ }); - std::eprintln!("{}", self.ctx.func.display()); + //std::eprintln!("{}", self.ctx.func.display()); self.ctx.compile(isa, &mut self.ctrl_plane).unwrap(); let code = self.ctx.compiled_code().unwrap(); @@ -240,15 +266,15 @@ impl hblang::backend::Backend for Backend { } fn build_signature( - call_conv: cranelift_codegen::isa::CallConv, - sig: hblang::ty::Sig, - types: &hblang::ty::Types, - signature: &mut cranelift_codegen::ir::Signature, + call_conv: cc::isa::CallConv, + sig: hbty::Sig, + types: &hbty::Types, + signature: &mut cir::Signature, arg_meta: &mut Vec, ) -> bool { signature.clear(call_conv); match call_conv { - cranelift_codegen::isa::CallConv::SystemV => { + cc::isa::CallConv::SystemV => { x86_64::build_systemv_signature(sig, types, signature, arg_meta) } _ => todo!(), @@ -262,18 +288,18 @@ struct AbiMeta { } struct FuncBuilder<'a, 'b> { - bl: cranelift_frontend::FunctionBuilder<'b>, + bl: cf::FunctionBuilder<'b>, isa: &'a dyn TargetIsa, - nodes: &'a hblang::nodes::Nodes, - tys: &'a hblang::ty::Types, - files: &'a hblang::utils::EntSlice, - values: &'b mut [Option>], + nodes: &'a hbnodes::Nodes, + tys: &'a hbty::Types, + files: &'a hbutils::EntSlice, + values: &'b mut [Option>], arg_lens: &'a [AbiMeta], stack_ret: bool, } impl FuncBuilder<'_, '_> { - pub fn build(mut self, sig: hblang::ty::Sig) { + pub fn build(mut self, sig: hbty::Sig) { let entry = self.bl.create_block(); self.bl.append_block_params_for_function_params(entry); self.bl.switch_to_block(entry); @@ -281,21 +307,21 @@ impl FuncBuilder<'_, '_> { if self.stack_ret { let ret_ptr = *arg_vals.take_first().unwrap(); - self.values[hblang::nodes::MEM as usize] = Some(Ok(ret_ptr)); + self.values[hbnodes::MEM as usize] = Some(Ok(ret_ptr)); } let Self { nodes, tys, .. } = self; - let mut parama_len = self.arg_lens[(self.tys.size_of(sig.ret) != 0) as usize..].iter(); + let mut parama_len = self.arg_lens[1..].iter(); let mut typs = sig.args.args(); - let mut args = nodes[hblang::nodes::VOID].outputs[hblang::nodes::ARG_START..].iter(); + let mut args = nodes[hbnodes::VOID].outputs[hbnodes::ARG_START..].iter(); while let Some(aty) = typs.next(tys) { - let hblang::ty::Arg::Value(ty) = aty else { continue }; + let hbty::Arg::Value(ty) = aty else { continue }; let abi_meta = parama_len.next().unwrap(); let &arg = args.next().unwrap(); if !abi_meta.trough_mem && ty.is_aggregate(tys) { - let slot = self.bl.create_sized_stack_slot(cranelift_codegen::ir::StackSlotData { - kind: cranelift_codegen::ir::StackSlotKind::ExplicitSlot, + let slot = self.bl.create_sized_stack_slot(cir::StackSlotData { + kind: cir::StackSlotKind::ExplicitSlot, size: self.tys.size_of(ty), align_shift: self.tys.align_of(ty).ilog2() as _, }); @@ -315,30 +341,30 @@ impl FuncBuilder<'_, '_> { } } - self.values[hblang::nodes::ENTRY as usize] = Some(Err(entry)); + self.values[hbnodes::ENTRY as usize] = Some(Err(entry)); - self.emit_node(hblang::nodes::VOID, hblang::nodes::VOID); + self.emit_node(hbnodes::VOID, hbnodes::VOID); self.bl.finalize(); } - fn value_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Value { + fn value_of(&self, nid: hbnodes::Nid) -> cir::Value { self.values[nid as usize].unwrap_or_else(|| panic!("{:?}", self.nodes[nid])).unwrap() } - fn block_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Block { + fn block_of(&self, nid: hbnodes::Nid) -> cir::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) { + fn close_block(&mut self, nid: hbnodes::Nid) { + if matches!(self.nodes[nid].kind, hbnodes::Kind::Loop) { 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::*; + fn emit_node(&mut self, nid: hbnodes::Nid, block: hbnodes::Nid) { + use hbnodes::*; let mut args = vec![]; if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { @@ -359,10 +385,9 @@ impl FuncBuilder<'_, '_> { 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[o as usize] = Some(Ok(self + .bl + .append_block_param(next, self.nodes[o].ty.to_clif(self.tys)))); } } self.bl.ins().jump(next, &args); @@ -405,7 +430,7 @@ impl FuncBuilder<'_, '_> { 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)))); + .append_block_param(next, self.nodes[o].ty.to_clif(self.tys)))); } } self.values[nid as usize] = Some(Err(next)); @@ -434,7 +459,7 @@ impl FuncBuilder<'_, '_> { } Kind::Return { .. } => { let mut ir_args = vec![]; - if node.inputs[1] == hblang::nodes::VOID { + if node.inputs[1] == hbnodes::VOID { } else { let abi_meta = self.arg_lens[0]; let arg = node.inputs[1]; @@ -455,14 +480,25 @@ impl FuncBuilder<'_, '_> { )); offset += align as i32; } + } else if self.stack_ret { + let src = self.value_of(self.nodes[arg].inputs[1]); + let dest = self.value_of(MEM); + self.bl.emit_small_memory_copy( + self.isa.frontend_config(), + dest, + src, + self.tys.size_of(self.nodes[arg].ty) as _, + self.tys.align_of(self.nodes[arg].ty) as _, + self.tys.align_of(self.nodes[arg].ty) as _, + false, + MemFlags::new(), + ); } else { ir_args.push(self.value_of(arg)); } } - let ret = self.value_of(node.inputs[1]); - - self.bl.ins().return_(&[ret]); + self.bl.ins().return_(&ir_args); self.close_block(block); self.emit_node(node.outputs[0], block); Err(self.block_of(block)) @@ -484,8 +520,7 @@ impl FuncBuilder<'_, '_> { todo!() } else { let mut arg_lens = vec![]; - let mut signature = - cranelift_codegen::ir::Signature::new(self.isa.default_call_conv()); + let mut signature = cir::Signature::new(self.isa.default_call_conv()); let stack_ret = build_signature( self.isa.default_call_conv(), self.tys.ins.funcs[func].sig, @@ -494,29 +529,28 @@ impl FuncBuilder<'_, '_> { &mut arg_lens, ); - let func_ref = 'b: { - let user_name_ref = self.bl.func.declare_imported_user_function( - cranelift_codegen::ir::UserExternalName { - namespace: 0, - index: func.index() as _, - }, - ); + let func_ref = + 'b: { + let user_name_ref = self.bl.func.declare_imported_user_function( + cir::UserExternalName { namespace: 0, index: func.index() as _ }, + ); - if let Some(id) = self.bl.func.dfg.ext_funcs.keys().find(|&k| { - self.bl.func.dfg.ext_funcs[k].name - == cranelift_codegen::ir::ExternalName::user(user_name_ref) - }) { - break 'b id; - } + if let Some(id) = self.bl.func.dfg.ext_funcs.keys().find(|&k| { + self.bl.func.dfg.ext_funcs[k].name + == cir::ExternalName::user(user_name_ref) + }) { + break 'b id; + } - let signature = self.bl.func.import_signature(signature.clone()); + let signature = self.bl.func.import_signature(signature.clone()); - self.bl.func.import_function(cranelift_codegen::ir::ExtFuncData { - name: cranelift_codegen::ir::ExternalName::user(user_name_ref), - signature, - colocated: true, - }) - }; + self.bl.func.import_function(cir::ExtFuncData { + name: cir::ExternalName::user(user_name_ref), + signature, + // somehow, this works + colocated: true, // !self.tys.ins.funcs[func].is_import, + }) + }; let mut ir_args = vec![]; @@ -525,13 +559,15 @@ impl FuncBuilder<'_, '_> { } let mut params = signature.params.as_slice(); - let mut parama_len = - arg_lens[(self.tys.size_of(node.ty) != 0) as usize..].iter(); + let mut parama_len = arg_lens[1..].iter(); let mut typs = args.args(); let mut args = node.inputs[1..].iter(); while let Some(aty) = typs.next(self.tys) { - let hblang::ty::Arg::Value(ty) = aty else { continue }; + let hbty::Arg::Value(ty) = aty else { continue }; let abi_meta = parama_len.next().unwrap(); + if abi_meta.arg_count == 0 { + continue; + } let &arg = args.next().unwrap(); if !abi_meta.trough_mem && ty.is_aggregate(self.tys) { let loc = params.take(..abi_meta.arg_count).unwrap(); @@ -590,101 +626,74 @@ impl FuncBuilder<'_, '_> { return; } } - 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(), + Kind::CInt { value } if self.nodes[nid].ty.is_float() => { + 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::CInt { value } => Ok(self.bl.ins().iconst( + cir::Type::int(self.tys.size_of(node.ty) as u16 * 8).unwrap_or_else(|| { + panic!("{}", hbty::Display::new(self.tys, self.files, node.ty),) + }), 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 { op } => { let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; - let [lhs, rhs] = [self.value_of(lhs), self.value_of(rhs)]; - assert!( - node.ty.is_integer() || node.ty == hblang::ty::Id::BOOL, - "TODO: unsupported binary type {}", - hblang::ty::Display::new(self.tys, self.files, node.ty) - ); + let [lh, rh] = [self.value_of(lhs), self.value_of(rhs)]; - use cranelift_codegen::ir::condcodes::IntCC as ICC; - fn icc_of(op: TokenKind, signed: bool) -> ICC { - match op { - TokenKind::Lt if signed => ICC::SignedLessThan, - TokenKind::Gt if signed => ICC::SignedGreaterThan, - TokenKind::Le if signed => ICC::SignedLessThanOrEqual, - TokenKind::Ge if signed => ICC::SignedGreaterThanOrEqual, + let is_int_op = node.ty.is_integer() + || (node.ty == hbty::Id::BOOL + && (self.nodes[lhs].ty.is_integer() + || self.nodes[lhs].ty == hbty::Id::BOOL)); + let is_float_op = node.ty.is_float() + || (node.ty == hbty::Id::BOOL && self.nodes[lhs].ty.is_float()); - TokenKind::Lt => ICC::UnsignedLessThan, - TokenKind::Gt => ICC::UnsignedGreaterThan, - TokenKind::Le => ICC::UnsignedLessThanOrEqual, - TokenKind::Ge => ICC::UnsignedGreaterThanOrEqual, - - TokenKind::Eq => ICC::Equal, - TokenKind::Ne => ICC::NotEqual, - _ => unreachable!(), - } - } - - use cranelift_codegen::ir::condcodes::FloatCC as FCC; - fn fcc_of(op: TokenKind) -> FCC { - match op { - TokenKind::Lt => FCC::LessThan, - TokenKind::Gt => FCC::GreaterThan, - TokenKind::Le => FCC::LessThanOrEqual, - TokenKind::Ge => FCC::GreaterThanOrEqual, - TokenKind::Eq => FCC::Equal, - TokenKind::Ne => FCC::NotEqual, - _ => unreachable!(), - } - } - - Ok(if node.ty.is_integer() { + Ok(if is_int_op { let signed = node.ty.is_signed(); match op { - TokenKind::Add => self.bl.ins().iadd(lhs, rhs), - TokenKind::Sub => self.bl.ins().isub(lhs, rhs), - TokenKind::Mul => self.bl.ins().imul(lhs, rhs), - TokenKind::Shl => self.bl.ins().ishl(lhs, rhs), - TokenKind::Xor => self.bl.ins().bxor(lhs, rhs), - TokenKind::Band => self.bl.ins().band(lhs, rhs), - TokenKind::Bor => self.bl.ins().bor(lhs, rhs), + TokenKind::Add => self.bl.ins().iadd(lh, rh), + TokenKind::Sub => self.bl.ins().isub(lh, rh), + TokenKind::Mul => self.bl.ins().imul(lh, rh), + TokenKind::Shl => self.bl.ins().ishl(lh, rh), + TokenKind::Xor => self.bl.ins().bxor(lh, rh), + TokenKind::Band => self.bl.ins().band(lh, rh), + TokenKind::Bor => self.bl.ins().bor(lh, rh), - TokenKind::Div if signed => self.bl.ins().sdiv(lhs, rhs), - TokenKind::Mod if signed => self.bl.ins().srem(lhs, rhs), - TokenKind::Shr if signed => self.bl.ins().sshr(lhs, rhs), + TokenKind::Div if signed => self.bl.ins().sdiv(lh, rh), + TokenKind::Mod if signed => self.bl.ins().srem(lh, rh), + TokenKind::Shr if signed => self.bl.ins().sshr(lh, rh), - TokenKind::Div => self.bl.ins().udiv(lhs, rhs), - TokenKind::Mod => self.bl.ins().urem(lhs, rhs), - TokenKind::Shr => self.bl.ins().ushr(lhs, rhs), + TokenKind::Div => self.bl.ins().udiv(lh, rh), + TokenKind::Mod => self.bl.ins().urem(lh, rh), + TokenKind::Shr => self.bl.ins().ushr(lh, rh), TokenKind::Lt | TokenKind::Gt | TokenKind::Le | TokenKind::Ge | TokenKind::Eq - | TokenKind::Ne => self.bl.ins().icmp(icc_of(op, signed), lhs, rhs), + | TokenKind::Ne => self.bl.ins().icmp(op.to_int_cc(signed), lh, rh), op => todo!("{op}"), } - } else if node.ty.is_float() { + } else if is_float_op { match op { - TokenKind::Add => self.bl.ins().fadd(lhs, rhs), - TokenKind::Sub => self.bl.ins().fsub(lhs, rhs), - TokenKind::Mul => self.bl.ins().fmul(lhs, rhs), - TokenKind::Div => self.bl.ins().fdiv(lhs, rhs), + TokenKind::Add => self.bl.ins().fadd(lh, rh), + TokenKind::Sub => self.bl.ins().fsub(lh, rh), + TokenKind::Mul => self.bl.ins().fmul(lh, rh), + TokenKind::Div => self.bl.ins().fdiv(lh, rh), TokenKind::Lt | TokenKind::Gt | TokenKind::Le | TokenKind::Ge | TokenKind::Eq - | TokenKind::Ne => self.bl.ins().fcmp(fcc_of(op), lhs, rhs), + | TokenKind::Ne => self.bl.ins().fcmp(op.to_float_cc(), lh, rh), op => todo!("{op}"), } } else { - todo!() + todo!("{}", hbty::Display::new(self.tys, self.files, node.ty)) }) } Kind::RetVal => Ok(self.value_of(node.inputs[0])), @@ -696,7 +705,7 @@ impl FuncBuilder<'_, '_> { .inner_of(self.nodes[node.inputs[1]].ty) .unwrap_or(self.nodes[node.inputs[1]].ty); - let dty = ty_to_clif_ty(dst, self.tys); + let dty = dst.to_clif(self.tys); Ok(match op { TokenKind::Sub => self.bl.ins().ineg(oper), TokenKind::Not => self.bl.ins().bnot(oper), @@ -715,55 +724,52 @@ impl FuncBuilder<'_, '_> { self.bl.ins().sextend(dty, oper) } TokenKind::Number - if (src.is_unsigned() || src == hblang::ty::Id::BOOL) + if (src.is_unsigned() || src == hbty::Id::BOOL) && (dst.is_integer() || dst.is_pointer()) => { self.bl.ins().uextend(dty, oper) } - TokenKind::Float if dst == hblang::ty::Id::F64 && src.is_float() => { + TokenKind::Float if dst == hbty::Id::F64 && src.is_float() => { self.bl.ins().fpromote(dty, oper) } - TokenKind::Float if dst == hblang::ty::Id::F32 && src.is_float() => { + TokenKind::Float if dst == hbty::Id::F32 && src.is_float() => { self.bl.ins().fdemote(dty, oper) } _ => todo!(), }) } Kind::Stck => { - let slot = self.bl.create_sized_stack_slot(cranelift_codegen::ir::StackSlotData { - kind: cranelift_codegen::ir::StackSlotKind::ExplicitSlot, + let slot = self.bl.create_sized_stack_slot(cir::StackSlotData { + kind: cir::StackSlotKind::ExplicitSlot, size: self.tys.size_of(node.ty), align_shift: self.tys.align_of(node.ty).ilog2() as _, }); - Ok(self.bl.ins().stack_addr(cranelift_codegen::ir::types::I64, slot, 0)) + Ok(self.bl.ins().stack_addr(cir::types::I64, slot, 0)) } Kind::Global { global } => { let glob_ref = { // already deduplicated by the SoN let colocated = true; - let user_name_ref = self.bl.func.declare_imported_user_function( - cranelift_codegen::ir::UserExternalName { + let user_name_ref = + self.bl.func.declare_imported_user_function(cir::UserExternalName { namespace: 1, index: global.index() as u32, - }, - ); - self.bl.func.create_global_value( - cranelift_codegen::ir::GlobalValueData::Symbol { - name: cranelift_codegen::ir::ExternalName::user(user_name_ref), - offset: cranelift_codegen::ir::immediates::Imm64::new(0), - colocated, - tls: false, - }, - ) + }); + self.bl.func.create_global_value(cir::GlobalValueData::Symbol { + name: cir::ExternalName::user(user_name_ref), + offset: cir::immediates::Imm64::new(0), + colocated, + tls: false, + }) }; - Ok(self.bl.ins().global_value(cranelift_codegen::ir::types::I64, glob_ref)) + Ok(self.bl.ins().global_value(cir::types::I64, glob_ref)) } Kind::Load if node.ty.is_aggregate(self.tys) => return, Kind::Load => { let ptr = self.value_of(node.inputs[1]); - Ok(self.bl.ins().load(ty_to_clif_ty(node.ty, self.tys), MemFlags::new(), ptr, 0)) + Ok(self.bl.ins().load(node.ty.to_clif(self.tys), MemFlags::new(), ptr, 0)) } Kind::Stre if node.ty.is_aggregate(self.tys) => { let src = self.value_of(self.nodes[node.inputs[1]].inputs[1]); @@ -792,22 +798,72 @@ impl FuncBuilder<'_, '_> { } } -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!() +trait ToCondcodes { + fn to_int_cc(self, signed: bool) -> condcodes::IntCC; + fn to_float_cc(self) -> condcodes::FloatCC; +} + +impl ToCondcodes for TokenKind { + fn to_int_cc(self, signed: bool) -> condcodes::IntCC { + use condcodes::IntCC as ICC; + match self { + Self::Lt if signed => ICC::SignedLessThan, + Self::Gt if signed => ICC::SignedGreaterThan, + Self::Le if signed => ICC::SignedLessThanOrEqual, + Self::Ge if signed => ICC::SignedGreaterThanOrEqual, + + Self::Lt => ICC::UnsignedLessThan, + Self::Gt => ICC::UnsignedGreaterThan, + Self::Le => ICC::UnsignedLessThanOrEqual, + Self::Ge => ICC::UnsignedGreaterThanOrEqual, + + Self::Eq => ICC::Equal, + Self::Ne => ICC::NotEqual, + _ => unreachable!(), + } + } + + fn to_float_cc(self) -> condcodes::FloatCC { + use condcodes::FloatCC as FCC; + match self { + Self::Lt => FCC::LessThan, + Self::Gt => FCC::GreaterThan, + Self::Le => FCC::LessThanOrEqual, + Self::Ge => FCC::GreaterThanOrEqual, + Self::Eq => FCC::Equal, + Self::Ne => FCC::NotEqual, + _ => unreachable!(), + } + } +} + +trait ToClifTy { + fn to_clif(self, cx: &hbty::Types) -> cir::Type; +} + +impl ToClifTy for hbty::Id { + fn to_clif(self, cx: &hbty::Types) -> cir::Type { + debug_assert!(!self.is_aggregate(cx)); + if self.is_integer() | self.is_pointer() | self.is_optional() || self == hbty::Id::BOOL { + cir::Type::int(cx.size_of(self) as u16 * 8).unwrap() + } else if self == hbty::Id::F32 { + cir::types::F32 + } else if self == hbty::Id::F64 { + cir::types::F64 + } else { + unimplemented!("{:?}", self) + } } } #[derive(Default)] struct Global { - module_id: Option, + module_id: Option, } #[derive(Default)] struct FuncHeaders { - module_id: Option, + module_id: Option, alignment: u32, code: Range, relocs: Range, @@ -816,19 +872,14 @@ struct FuncHeaders { #[derive(Default)] struct Functions { - headers: EntVec, + 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, - ) { + fn push(&mut self, id: hbty::Func, func: &cir::Function, code: &MachBufferFinalized) { self.headers.shadow(id.index() + 1); self.headers[id] = FuncHeaders { module_id: None, @@ -847,9 +898,9 @@ impl Functions { #[derive(Default)] struct Assembler { name: String, - frontier: Vec, - globals: Vec, - funcs: Vec, + frontier: Vec, + globals: Vec, + funcs: Vec, } #[derive(Debug)] @@ -857,6 +908,7 @@ pub enum BackendCreationError { UnsupportedTriplet(LookupError), InvalidFlags(CodegenError), UnsupportedModuleConfig(ModuleError), + InvalidFlag { key: String, value: String, err: SetError }, } impl Display for BackendCreationError { @@ -871,6 +923,13 @@ impl Display for BackendCreationError { BackendCreationError::UnsupportedModuleConfig(err) => { write!(f, "Unsupported module configuration: {}", err) } + BackendCreationError::InvalidFlag { key, value, err } => { + write!( + f, + "Problem setting a '{key}' to '{value}': {err}\navailable flags: {}", + cc::settings::Flags::new(cc::settings::builder()) + ) + } } } } diff --git a/cranelift-backend/src/x86_64.rs b/cranelift-backend/src/x86_64.rs index 3c48f0b..9fcb416 100644 --- a/cranelift-backend/src/x86_64.rs +++ b/cranelift-backend/src/x86_64.rs @@ -1,7 +1,7 @@ // The classification code for the x86_64 ABI is taken from the clay language // https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp -use crate::AbiMeta; +use {crate::AbiMeta, hblang::ty}; pub fn build_systemv_signature( sig: hblang::ty::Sig, @@ -12,11 +12,14 @@ pub fn build_systemv_signature( 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; + let stack_ret = signature.returns.len() == 1 + && signature.returns[0].purpose == cranelift_codegen::ir::ArgumentPurpose::StructReturn; if stack_ret { signature.params.append(&mut signature.returns); + arg_lens.push(AbiMeta { arg_count: signature.params.len(), trough_mem: true }); + } else { + arg_lens.push(AbiMeta { arg_count: signature.returns.len(), trough_mem: false }); } let mut args = sig.args.args(); @@ -66,7 +69,7 @@ fn classify_arg( let mut c = match layout.expand() { _ if size == 0 => return Ok(()), - _ if layout.is_integer() || layout.is_pointer() => Class::Int, + _ if layout.is_integer() || layout.is_pointer() || layout == ty::Id::BOOL => Class::Int, _ if layout.is_float() => Class::Sse, hblang::ty::Kind::Struct(s) => { @@ -115,7 +118,7 @@ fn classify_arg( return Ok(()); } - _ => unimplemented!(), + ty => unimplemented!("{ty:?}"), }; // Fill in `cls` for scalars (Int/Sse) and vectors (Sse). @@ -241,6 +244,10 @@ impl Alloca { cx: &hblang::ty::Types, dest: &mut Vec, ) -> bool { + if cx.size_of(arg) == 0 { + return false; + } + let mut cls_or_mem = classify_arg(cx, arg); if is_arg { diff --git a/examples/raylib/main.hb b/examples/raylib/main.hb new file mode 100644 index 0000000..04ce9f6 --- /dev/null +++ b/examples/raylib/main.hb @@ -0,0 +1,47 @@ +InitWindow := fn(w: uint, h: uint, name: ^u8): uint @import() +WindowShouldClose := fn(): bool @import() +BeginDrawing := fn(): void @import() +EndDrawing := fn(): void @import() +DrawRectangleV := fn(pos: Vec2, size: Vec2, color: Color): void @import() +DrawRectangle := fn(a: uint, b: uint, c: uint, d: uint, color: Color): void @import() +ClearBackground := fn(color: Color): void @import() +SetTargetFPS := fn(target: uint): void @import() +GetFrameTime := fn(): f32 @import() + +Vec2 := struct {x: f32, y: f32} +Color := struct {r: u8, g: u8, b: u8, a: u8} + +$W := 800 +$H := 600 + +main := fn(): uint { + _ = InitWindow(W, H, "whawee\0".ptr) + + SetTargetFPS(60) + + pos := Vec2.(100, 100) + vel := Vec2.(300, 300) + size := Vec2.(100, 100) + color := Color.(17, 255, 17, 255) + + loop if WindowShouldClose() break else { + BeginDrawing() + ClearBackground(.(0, 0, 0, 255)) + + DrawRectangleV(pos, size, color) + pos += vel * .(GetFrameTime(), GetFrameTime()) + + if pos.x < 0 | pos.x + size.x > W { + vel.x *= -1 + color += .(32, 11, 20, 0) + } + + if pos.y < 0 | pos.y + size.y > H { + vel.y *= -1 + color += .(32, 11, 20, 0) + } + EndDrawing() + } + + return 0 +} diff --git a/examples/raylib/run.sh b/examples/raylib/run.sh new file mode 100755 index 0000000..6ec14ea --- /dev/null +++ b/examples/raylib/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash +DIR=$(dirname $0) +cd $DIR +cargo run -p hbc main.hb > out.o && gcc -o main out.o -lraylib -lm -ldl -lpthread -lrt -lGL -lX11 && ./main diff --git a/lang/command-help.txt b/lang/command-help.txt deleted file mode 100644 index 7bb66b9..0000000 --- a/lang/command-help.txt +++ /dev/null @@ -1,5 +0,0 @@ ---fmt - format all imported source files ---fmt-stdout - dont write the formatted file but print it ---dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler) ---threads <1...> - number of extra threads compiler can use [default: 0] ---path-resolver - choose between builtin path resolvers, options are: ableos diff --git a/lang/src/backend/hbvm.rs b/lang/src/backend/hbvm.rs index f3a6380..52ffe59 100644 --- a/lang/src/backend/hbvm.rs +++ b/lang/src/backend/hbvm.rs @@ -106,7 +106,7 @@ pub struct HbvmBackend { offsets: Vec, } -pub const TARGET_TRIPLE: &str = "hbvm-virt-ableos"; +pub const TARGET_TRIPLE: &str = "unknown-virt-unknown"; impl HbvmBackend { fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { diff --git a/lang/src/fs.rs b/lang/src/fs.rs index 8134860..baafd14 100644 --- a/lang/src/fs.rs +++ b/lang/src/fs.rs @@ -6,7 +6,7 @@ use { ty, FnvBuildHasher, }, alloc::{string::String, vec::Vec}, - core::{fmt::Write, num::NonZeroUsize, ops::Deref}, + core::{fmt::Write, ops::Deref}, hashbrown::hash_map, std::{ borrow::ToOwned, @@ -74,62 +74,6 @@ pub struct Options<'a> { pub backend: Option<&'a mut dyn Backend>, } -impl<'a> Options<'a> { - pub fn from_args( - 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...] ")?; - writeln!(out, include_str!("../command-help.txt"))?; - return Err(std::io::ErrorKind::Other.into()); - } - - Ok(Options { - fmt: args.contains(&"--fmt"), - fmt_stdout: args.contains(&"--fmt-stdout"), - dump_asm: args.contains(&"--dump-asm"), - extra_threads: args - .iter() - .position(|&a| a == "--threads") - .map(|i| { - args[i + 1].parse::().map_err(|e| { - writeln!(out, "--threads expects non zero integer: {e}") - .err() - .unwrap_or(std::io::ErrorKind::Other.into()) - }) - }) - .transpose()? - .map_or(1, NonZeroUsize::get) - - 1, - resolver: args - .iter() - .position(|&a| a == "--path-resolver") - .map(|i| { - resolvers.iter().find(|&&(n, _)| args[i + 1] == n).map(|&(_, r)| r).ok_or_else( - || { - writeln!( - out, - "--path-resolver can only be one of: {}", - resolvers - .iter() - .map(|&(n, _)| n) - .intersperse(", ") - .collect::() - ) - .err() - .unwrap_or(std::io::ErrorKind::Other.into()) - }, - ) - }) - .transpose()?, - backend, - }) - } -} - pub fn run_compiler( root_file: &str, options: Options, diff --git a/lang/src/parser.rs b/lang/src/parser.rs index a105c6a..8095fbc 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -257,7 +257,11 @@ impl<'a, 'b> Parser<'a, 'b> { None => { let ident = match Ident::new(token.start, name.len() as _) { None => { - self.report(token.start, "identifier can at most have 64 characters"); + self.report( + token.start, + "identifier can at most have 63 characters, \ + the code is too clean to efficiently represent in memory", + ); Ident::new(token.start, 63).unwrap() } Some(id) => id, diff --git a/lang/src/son.rs b/lang/src/son.rs index e5e9563..f50ca39 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -1202,14 +1202,6 @@ impl<'a> Codegen<'a> { let mut lhs = self.ptr_expr_ctx(left, ctx)?; self.implicit_unwrap(left.pos(), &mut lhs); - fn is_scalar_op(op: TokenKind, ty: ty::Id) -> bool { - ty.is_pointer() - || ty.is_integer() - || ty == ty::Id::BOOL - || (ty == ty::Id::TYPE && matches!(op, TokenKind::Eq | TokenKind::Ne)) - || (ty.is_float() && op.is_supported_float_op()) - } - match lhs.ty.expand() { ty::Kind::Struct(s) if op.is_homogenous() => { debug_assert!(lhs.ptr); @@ -3095,7 +3087,7 @@ impl<'a> Codegen<'a> { let rhs = self.offset(rhs, off); let dst = self.offset(dst, off); match ty.expand() { - _ if ty.is_pointer() || ty.is_integer() || ty == ty::Id::BOOL => { + _ if is_scalar_op(op, ty) => { let lhs = self.load_mem(lhs, ty); let rhs = self.load_mem(rhs, ty); let res = @@ -3279,6 +3271,7 @@ impl<'a> Codegen<'a> { sig: Sig { args, ret }, is_inline, is_generic: true, + is_import: false, comp_state: Default::default(), }) .into() @@ -4238,32 +4231,49 @@ impl<'a> Codegen<'a> { }, |s, base| s.ins.unions.push(UnionData { base, ..Default::default() }), ), - Expr::Closure { pos, args, ret, .. } if let Some(name) = sc.name => { - let sig = 'b: { - let arg_base = self.tys.tmp.args.len(); - for arg in args { - let sym = parser::find_symbol(&self.files[sc.file].symbols, arg.id); - if sym.flags & idfl::COMPTIME != 0 { - self.tys.tmp.args.truncate(arg_base); - break 'b None; - } - let ty = self.parse_ty(sc.anon(), &arg.ty); - if ty == ty::Id::ANY_TYPE { - break 'b None; - } - self.tys.tmp.args.push(ty); - } - - let Some(args) = self.tys.pack_args(arg_base) else { - return self.error_low(sc.file, pos, "function has too many argumnets"); - }; - let ret = self.parse_ty(sc.anon(), ret); - - Some(Sig { args, ret }) + Expr::Closure { + pos, + args, + ret, + body: &Expr::Directive { name: "import", args: import_args, .. }, + } if let Some(name) = sc.name => { + let Some(sig) = self.try_parse_concrete_signature(pos, sc, args, ret) else { + return self.error_low( + sc.file, + pos, + "I am too tired to deal with your bulllshit", + ); }; - //let returns_type = matches!(ret, &Expr::Ident { id, .. } if ); - match sig { + let name = if let &[Expr::String { pos, literal }] = import_args { + let Some(name) = Ident::new(pos + 1, literal.len() as u32 - 2) else { + return self.error_low( + sc.file, + pos, + "the simbol name is limmited to 63 characters, please don't import java", + ); + }; + name + } else { + name + }; + + let func = FuncData { + file: sc.file, + parent: sc.parent, + name, + pos, + sig, + expr: ExprRef::new(expr), + is_inline: sc.is_ct, + is_generic: false, + is_import: true, + comp_state: [CompState::Compiled.into(); 2], + }; + self.tys.ins.funcs.push(func).into() + } + Expr::Closure { pos, args, ret, .. } if let Some(name) = sc.name => { + match self.try_parse_concrete_signature(pos, sc, args, ret) { Some(sig) => { let func = FuncData { file: sc.file, @@ -4274,6 +4284,7 @@ impl<'a> Codegen<'a> { expr: ExprRef::new(expr), is_inline: sc.is_ct, is_generic: false, + is_import: false, comp_state: Default::default(), }; self.tys.ins.funcs.push(func).into() @@ -4310,6 +4321,36 @@ impl<'a> Codegen<'a> { } } + fn try_parse_concrete_signature( + &mut self, + pos: Pos, + sc: TyScope, + args: &[parser::Arg], + ret: &Expr, + ) -> Option { + let arg_base = self.tys.tmp.args.len(); + for arg in args { + let sym = parser::find_symbol(&self.files[sc.file].symbols, arg.id); + if sym.flags & idfl::COMPTIME != 0 { + self.tys.tmp.args.truncate(arg_base); + return None; + } + let ty = self.parse_ty(sc.anon(), &arg.ty); + if ty == ty::Id::ANY_TYPE { + return None; + } + self.tys.tmp.args.push(ty); + } + + let Some(args) = self.tys.pack_args(arg_base) else { + self.error_low(sc.file, pos, "function has too many argumnets"); + return None; + }; + let ret = self.parse_ty(sc.anon(), ret); + + Some(Sig { args, ret }) + } + #[expect(clippy::too_many_arguments)] fn parse_base_ty>( &mut self, @@ -4396,6 +4437,14 @@ impl TyScope { } } +fn is_scalar_op(op: TokenKind, ty: ty::Id) -> bool { + ty.is_pointer() + || ty.is_integer() + || ty == ty::Id::BOOL + || (ty == ty::Id::TYPE && matches!(op, TokenKind::Eq | TokenKind::Ne)) + || (ty.is_float() && op.is_supported_float_op()) +} + #[cfg(test)] mod tests { use { diff --git a/lang/src/ty.rs b/lang/src/ty.rs index d8b621b..1b99de7 100644 --- a/lang/src/ty.rs +++ b/lang/src/ty.rs @@ -671,6 +671,7 @@ pub struct FuncData { pub sig: Sig, pub is_inline: bool, pub is_generic: bool, + pub is_import: bool, pub comp_state: [PackedCompState; 2], } @@ -928,6 +929,7 @@ impl Types { | Kind::Slice(_) | Kind::Tuple(_) | Kind::Opt(_) => utils::is_pascal_case, + Kind::Func(f) if self.ins.funcs[f].is_import => |_| Ok(()), Kind::Func(f) if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } = self.ins.funcs[f].expr.get(&files[self.ins.funcs[f].file]) @@ -1127,7 +1129,7 @@ impl Types { } } Kind::Opt(opt) => self.align_of(self.ins.opts[opt].base), - Kind::Builtin(_) | Kind::Enum(_) | Kind::Ptr(_) => self.size_of(ty), + Kind::Builtin(_) | Kind::Enum(_) | Kind::Ptr(_) => self.size_of(ty).max(1), Kind::Func(_) | Kind::Template(_) | Kind::Global(_) diff --git a/smh.hb b/smh.hb deleted file mode 100644 index c969e3e..0000000 --- a/smh.hb +++ /dev/null @@ -1,15 +0,0 @@ -main := fn(): uint { - return fib(10) -} - -fib := fn(x: uint): uint { - a := 0 - b := 1 - loop if x == 0 break else { - c := a + b - a = b - b = c - x -= 1 - } - return a -}