polishing the compiler cli (cheating with clap)

also adding the raylib example

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-30 13:26:38 +01:00
parent d25540dd52
commit 58479deca1
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
15 changed files with 639 additions and 370 deletions

1
.gitignore vendored
View file

@ -4,6 +4,7 @@ rustc-ice-*
a.out a.out
out.o out.o
/examples/raylib/main
# sqlite # sqlite
db.sqlite db.sqlite

137
Cargo.lock generated
View file

@ -44,6 +44,55 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 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]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.95" version = "1.0.95"
@ -323,6 +372,46 @@ dependencies = [
"libloading", "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]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.51" version = "0.1.51"
@ -332,6 +421,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "const_format" name = "const_format"
version = "0.2.33" version = "0.2.33"
@ -572,7 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -769,6 +864,7 @@ version = "0.1.0"
name = "hbc" name = "hbc"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap",
"cranelift-backend", "cranelift-backend",
"hblang", "hblang",
"log", "log",
@ -818,7 +914,7 @@ version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [ dependencies = [
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -945,6 +1041,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.1" version = "0.12.1"
@ -1086,7 +1188,7 @@ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
"wasi", "wasi",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1288,7 +1390,7 @@ dependencies = [
"libc", "libc",
"spin", "spin",
"untrusted", "untrusted",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1333,7 +1435,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1477,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1492,6 +1594,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -1559,7 +1667,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1690,6 +1798,12 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@ -1805,6 +1919,15 @@ dependencies = [
"windows-targets", "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]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"

View file

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
clap = { version = "4.5.23", features = ["derive", "env"] }
cranelift-backend = { version = "0.1.0", path = "../cranelift-backend" } cranelift-backend = { version = "0.1.0", path = "../cranelift-backend" }
hblang = { workspace = true, features = ["std"] } hblang = { workspace = true, features = ["std"] }
log = "0.4.22" log = "0.4.22"

View file

@ -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 `<key>=<value>,...` 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<String>,
/// 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() { fn main() {
use std::io::Write; use std::io::Write;
fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> { fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> {
let args = std::env::args().collect::<Vec<_>>(); let Args {
let args = args.iter().map(String::as_str).collect::<Vec<_>>(); backend_flags,
target,
path_resolver,
fmt,
fmt_stdout,
dump_asm,
extra_threads,
file,
} = Args::parse();
let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)]; let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)];
let mut native = None; let mut native = None;
let backend = match args.iter().position(|&v| v == "--target").map(|i| args[i + 1]) { let backend = if target
Some(hblang::backend::hbvm::TARGET_TRIPLE) => None, == target_lexicon::Triple::from_str(hblang::backend::hbvm::TARGET_TRIPLE).unwrap()
Some(target) => Some( {
None
} else {
Some(
native.insert( 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)?, .map_err(io::Error::other)?,
) as &mut dyn hblang::backend::Backend, ) 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 opts = hblang::Options {
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"); 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(); log::set_logger(&hblang::fs::Logger).unwrap();
@ -38,9 +85,10 @@ fn main() {
std::io::stderr().write_all(warnings.as_bytes()).unwrap(); std::io::stderr().write_all(warnings.as_bytes()).unwrap();
std::io::stdout().write_all(&out).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(warnings.as_bytes()).unwrap();
std::io::stderr().write_all(&out).unwrap(); std::io::stderr().write_all(&out).unwrap();
std::eprint!("{e}");
std::process::exit(1); std::process::exit(1);
} }
} }

View file

@ -3,51 +3,59 @@
use { use {
core::panic, core::panic,
cranelift_codegen::{ cranelift_codegen::{
CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, self as cc, CodegenError, Final, FinalizedMachReloc, MachBufferFinalized,
ir::{InstBuilder, MemFlags, TrapCode, UserExternalName}, ir::{self as cir, InstBuilder, MemFlags, TrapCode, UserExternalName, condcodes},
isa::{LookupError, TargetIsa}, isa::{LookupError, TargetIsa},
settings::Configurable, settings::{Configurable, SetError},
}, },
cranelift_frontend::FunctionBuilder, cranelift_frontend::{self as cf, FunctionBuilder},
cranelift_module::{Module, ModuleError}, cranelift_module::{self as cm, Module, ModuleError},
hblang::{ hblang::{
lexer::TokenKind, lexer::TokenKind,
nodes::Kind, nodes::{self as hbnodes},
utils::{Ent, EntVec}, ty as hbty,
utils::{self as hbutils, Ent, EntVec},
},
std::{
fmt::{Display, Write},
ops::Range,
}, },
std::{fmt::Display, ops::Range},
}; };
mod x86_64; mod x86_64;
pub struct Backend { pub struct Backend {
ctx: cranelift_codegen::Context, ctx: cc::Context,
dt_ctx: cranelift_module::DataDescription, dt_ctx: cm::DataDescription,
fb_ctx: cranelift_frontend::FunctionBuilderContext, fb_ctx: cf::FunctionBuilderContext,
module: Option<cranelift_object::ObjectModule>, module: Option<cranelift_object::ObjectModule>,
ctrl_plane: cranelift_codegen::control::ControlPlane, ctrl_plane: cc::control::ControlPlane,
funcs: Functions, funcs: Functions,
globals: EntVec<hblang::ty::Global, Global>, globals: EntVec<hbty::Global, Global>,
asm: Assembler, asm: Assembler,
} }
impl Backend { impl Backend {
pub fn new(triple: target_lexicon::Triple) -> Result<Self, BackendCreationError> { pub fn new(triple: target_lexicon::Triple, flags: &str) -> Result<Self, BackendCreationError> {
Ok(Self { Ok(Self {
ctx: cranelift_codegen::Context::new(), ctx: cc::Context::new(),
dt_ctx: cranelift_module::DataDescription::new(), dt_ctx: cm::DataDescription::new(),
fb_ctx: cranelift_frontend::FunctionBuilderContext::default(), fb_ctx: cf::FunctionBuilderContext::default(),
ctrl_plane: cranelift_codegen::control::ControlPlane::default(), ctrl_plane: cc::control::ControlPlane::default(),
module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new( module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new(
cranelift_codegen::isa::lookup(triple)?.finish( cc::isa::lookup(triple)?.finish(cc::settings::Flags::new({
cranelift_codegen::settings::Flags::new({ let mut bl = cc::settings::builder();
let mut bl = cranelift_codegen::settings::builder(); for (k, v) in flags.split(',').filter_map(|s| s.split_once('=')) {
bl.set("enable_verifier", "true").unwrap(); bl.set(k, v).map_err(|err| BackendCreationError::InvalidFlag {
bl key: k.to_owned(),
}), value: v.to_owned(),
)?, err,
})?;
}
bl
}))?,
"main", "main",
cranelift_module::default_libcall_names(), cm::default_libcall_names(),
)?) )?)
.into(), .into(),
funcs: Default::default(), funcs: Default::default(),
@ -60,9 +68,9 @@ impl Backend {
impl hblang::backend::Backend for Backend { impl hblang::backend::Backend for Backend {
fn assemble_reachable( fn assemble_reachable(
&mut self, &mut self,
from: hblang::ty::Func, from: hbty::Func,
types: &hblang::ty::Types, types: &hbty::Types,
files: &hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>, files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
to: &mut Vec<u8>, to: &mut Vec<u8>,
) -> hblang::backend::AssemblySpec { ) -> hblang::backend::AssemblySpec {
debug_assert!(self.asm.frontier.is_empty()); 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"); 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 { match name.namespace {
0 => hblang::ty::Kind::Func(hblang::ty::Func::new(name.index as _)), 0 => hbty::Kind::Func(hbty::Func::new(name.index as _)),
1 => hblang::ty::Kind::Global(hblang::ty::Global::new(name.index as _)), 1 => hbty::Kind::Global(hbty::Global::new(name.index as _)),
_ => unreachable!(), _ => unreachable!(),
} }
.compress() .compress()
@ -85,12 +93,16 @@ impl hblang::backend::Backend for Backend {
self.asm.frontier.push(from.into()); self.asm.frontier.push(from.into());
while let Some(itm) = self.asm.frontier.pop() { while let Some(itm) = self.asm.frontier.pop() {
match itm.expand() { 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 fuc = &mut self.funcs.headers[func];
let file = &files[fd.file];
if fuc.module_id.is_some() { if fuc.module_id.is_some() {
continue; continue;
} }
self.asm.funcs.push(func);
self.asm.frontier.extend( self.asm.frontier.extend(
fuc.external_names.clone().map(|r| { fuc.external_names.clone().map(|r| {
clif_name_to_ty(self.funcs.external_names[r as usize].clone()) 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(); self.asm.name.clear();
if func == from { if func == from {
self.asm.name.push_str("main"); self.asm.name.push_str("main");
} else if fd.is_import {
self.asm.name.push_str(file.ident_str(fd.name));
} else { } else {
let file = &files[types.ins.funcs[func].file];
self.asm.name.push_str(hblang::strip_cwd(&file.path)); self.asm.name.push_str(hblang::strip_cwd(&file.path));
self.asm.name.push('.'); 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 { let linkage = if func == from {
cranelift_module::Linkage::Export cm::Linkage::Export
} else if fd.is_import {
cm::Linkage::Import
} else { } else {
cranelift_module::Linkage::Local cm::Linkage::Local
}; };
build_signature( build_signature(
module.isa().default_call_conv(), module.isa().default_call_conv(),
types.ins.funcs[func].sig, fd.sig,
types, types,
&mut self.ctx.func.signature, &mut self.ctx.func.signature,
&mut vec![], &mut vec![],
@ -122,25 +137,31 @@ impl hblang::backend::Backend for Backend {
.declare_function(&self.asm.name, linkage, &self.ctx.func.signature) .declare_function(&self.asm.name, linkage, &self.ctx.func.signature)
.unwrap(), .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() { if self.globals[glob].module_id.is_some() {
continue; continue;
} }
self.asm.globals.push(glob); self.asm.globals.push(glob);
self.asm.name.clear(); self.asm.name.clear();
let file = &files[types.ins.globals[glob].file]; let mutable = if types.ins.globals[glob].file == Default::default() {
self.asm.name.push_str(hblang::strip_cwd(&file.path)); writeln!(self.asm.name, "anon{}", glob.index()).unwrap();
self.asm.name.push('.'); false
self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name)); } 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( self.globals[glob].module_id = Some(
module module
.declare_data( .declare_data(&self.asm.name, cm::Linkage::Local, mutable, false)
&self.asm.name,
cranelift_module::Linkage::Local,
true,
false,
)
.unwrap(), .unwrap(),
); );
} }
@ -150,14 +171,22 @@ impl hblang::backend::Backend for Backend {
for &func in &self.asm.funcs { for &func in &self.asm.funcs {
let fuc = &self.funcs.headers[func]; let fuc = &self.funcs.headers[func];
assert!(!types.ins.funcs[func].is_import);
debug_assert!(!fuc.code.is_empty()); debug_assert!(!fuc.code.is_empty());
let names = &mut self.funcs.external_names let names = &mut self.funcs.external_names
[fuc.external_names.start as usize..fuc.external_names.end as usize]; [fuc.external_names.start as usize..fuc.external_names.end as usize];
names.iter_mut().for_each(|nm| { self.ctx.func.clear();
nm.index = self.funcs.headers[hblang::ty::Func::new(nm.index as _)] names.iter().for_each(|nm| {
.module_id let mut nm = nm.clone();
.unwrap() if nm.namespace == 0 {
.as_u32(); 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()); self.ctx.func.params.ensure_user_func_name(nm.clone());
}); });
module module
@ -187,8 +216,8 @@ impl hblang::backend::Backend for Backend {
&'a self, &'a self,
_sluce: &[u8], _sluce: &[u8],
_eca_handler: &mut dyn FnMut(&mut &[u8]), _eca_handler: &mut dyn FnMut(&mut &[u8]),
_types: &'a hblang::ty::Types, _types: &'a hbty::Types,
_files: &'a hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>, _files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
_output: &mut String, _output: &mut String,
) -> Result<(), std::boxed::Box<dyn core::error::Error + Send + Sync + 'a>> { ) -> Result<(), std::boxed::Box<dyn core::error::Error + Send + Sync + 'a>> {
unimplemented!() unimplemented!()
@ -196,10 +225,10 @@ impl hblang::backend::Backend for Backend {
fn emit_body( fn emit_body(
&mut self, &mut self,
id: hblang::ty::Func, id: hbty::Func,
nodes: &hblang::nodes::Nodes, nodes: &hbnodes::Nodes,
tys: &hblang::ty::Types, tys: &hbty::Types,
files: &hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>, files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
) { ) {
self.ctx.clear(); self.ctx.clear();
let isa = self.module.as_ref().unwrap().isa(); let isa = self.module.as_ref().unwrap().isa();
@ -226,12 +255,9 @@ impl hblang::backend::Backend for Backend {
.build(tys.ins.funcs[id].sig); .build(tys.ins.funcs[id].sig);
self.ctx.func.name = self.ctx.func.name =
cranelift_codegen::ir::UserFuncName::User(cranelift_codegen::ir::UserExternalName { cir::UserFuncName::User(cir::UserExternalName { namespace: 0, index: id.index() as _ });
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(); self.ctx.compile(isa, &mut self.ctrl_plane).unwrap();
let code = self.ctx.compiled_code().unwrap(); let code = self.ctx.compiled_code().unwrap();
@ -240,15 +266,15 @@ impl hblang::backend::Backend for Backend {
} }
fn build_signature( fn build_signature(
call_conv: cranelift_codegen::isa::CallConv, call_conv: cc::isa::CallConv,
sig: hblang::ty::Sig, sig: hbty::Sig,
types: &hblang::ty::Types, types: &hbty::Types,
signature: &mut cranelift_codegen::ir::Signature, signature: &mut cir::Signature,
arg_meta: &mut Vec<AbiMeta>, arg_meta: &mut Vec<AbiMeta>,
) -> bool { ) -> bool {
signature.clear(call_conv); signature.clear(call_conv);
match call_conv { match call_conv {
cranelift_codegen::isa::CallConv::SystemV => { cc::isa::CallConv::SystemV => {
x86_64::build_systemv_signature(sig, types, signature, arg_meta) x86_64::build_systemv_signature(sig, types, signature, arg_meta)
} }
_ => todo!(), _ => todo!(),
@ -262,18 +288,18 @@ struct AbiMeta {
} }
struct FuncBuilder<'a, 'b> { struct FuncBuilder<'a, 'b> {
bl: cranelift_frontend::FunctionBuilder<'b>, bl: cf::FunctionBuilder<'b>,
isa: &'a dyn TargetIsa, isa: &'a dyn TargetIsa,
nodes: &'a hblang::nodes::Nodes, nodes: &'a hbnodes::Nodes,
tys: &'a hblang::ty::Types, tys: &'a hbty::Types,
files: &'a hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>, files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
values: &'b mut [Option<Result<cranelift_codegen::ir::Value, cranelift_codegen::ir::Block>>], values: &'b mut [Option<Result<cir::Value, cir::Block>>],
arg_lens: &'a [AbiMeta], arg_lens: &'a [AbiMeta],
stack_ret: bool, stack_ret: bool,
} }
impl FuncBuilder<'_, '_> { 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(); let entry = self.bl.create_block();
self.bl.append_block_params_for_function_params(entry); self.bl.append_block_params_for_function_params(entry);
self.bl.switch_to_block(entry); self.bl.switch_to_block(entry);
@ -281,21 +307,21 @@ impl FuncBuilder<'_, '_> {
if self.stack_ret { if self.stack_ret {
let ret_ptr = *arg_vals.take_first().unwrap(); 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 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 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) { 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 abi_meta = parama_len.next().unwrap();
let &arg = args.next().unwrap(); let &arg = args.next().unwrap();
if !abi_meta.trough_mem && ty.is_aggregate(tys) { if !abi_meta.trough_mem && ty.is_aggregate(tys) {
let slot = self.bl.create_sized_stack_slot(cranelift_codegen::ir::StackSlotData { let slot = self.bl.create_sized_stack_slot(cir::StackSlotData {
kind: cranelift_codegen::ir::StackSlotKind::ExplicitSlot, kind: cir::StackSlotKind::ExplicitSlot,
size: self.tys.size_of(ty), size: self.tys.size_of(ty),
align_shift: self.tys.align_of(ty).ilog2() as _, 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(); 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() 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() self.values[nid as usize].unwrap().unwrap_err()
} }
fn close_block(&mut self, nid: hblang::nodes::Nid) { fn close_block(&mut self, nid: hbnodes::Nid) {
if matches!(self.nodes[nid].kind, Kind::Loop) { if matches!(self.nodes[nid].kind, hbnodes::Kind::Loop) {
return; return;
} }
self.bl.seal_block(self.block_of(nid)); self.bl.seal_block(self.block_of(nid));
} }
fn emit_node(&mut self, nid: hblang::nodes::Nid, block: hblang::nodes::Nid) { fn emit_node(&mut self, nid: hbnodes::Nid, block: hbnodes::Nid) {
use hblang::nodes::*; use hbnodes::*;
let mut args = vec![]; let mut args = vec![];
if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) {
@ -359,10 +385,9 @@ impl FuncBuilder<'_, '_> {
let next = self.bl.create_block(); let next = self.bl.create_block();
for &o in self.nodes[nid].outputs.iter() { for &o in self.nodes[nid].outputs.iter() {
if self.nodes[o].is_data_phi() { if self.nodes[o].is_data_phi() {
self.values[o as usize] = Some(Ok(self.bl.append_block_param( self.values[o as usize] = Some(Ok(self
next, .bl
ty_to_clif_ty(self.nodes[o].ty, self.tys), .append_block_param(next, self.nodes[o].ty.to_clif(self.tys))));
)));
} }
} }
self.bl.ins().jump(next, &args); self.bl.ins().jump(next, &args);
@ -405,7 +430,7 @@ impl FuncBuilder<'_, '_> {
if self.nodes[o].is_data_phi() { if self.nodes[o].is_data_phi() {
self.values[o as usize] = Some(Ok(self self.values[o as usize] = Some(Ok(self
.bl .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)); self.values[nid as usize] = Some(Err(next));
@ -434,7 +459,7 @@ impl FuncBuilder<'_, '_> {
} }
Kind::Return { .. } => { Kind::Return { .. } => {
let mut ir_args = vec![]; let mut ir_args = vec![];
if node.inputs[1] == hblang::nodes::VOID { if node.inputs[1] == hbnodes::VOID {
} else { } else {
let abi_meta = self.arg_lens[0]; let abi_meta = self.arg_lens[0];
let arg = node.inputs[1]; let arg = node.inputs[1];
@ -455,14 +480,25 @@ impl FuncBuilder<'_, '_> {
)); ));
offset += align as i32; 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 { } else {
ir_args.push(self.value_of(arg)); ir_args.push(self.value_of(arg));
} }
} }
let ret = self.value_of(node.inputs[1]); self.bl.ins().return_(&ir_args);
self.bl.ins().return_(&[ret]);
self.close_block(block); self.close_block(block);
self.emit_node(node.outputs[0], block); self.emit_node(node.outputs[0], block);
Err(self.block_of(block)) Err(self.block_of(block))
@ -484,8 +520,7 @@ impl FuncBuilder<'_, '_> {
todo!() todo!()
} else { } else {
let mut arg_lens = vec![]; let mut arg_lens = vec![];
let mut signature = let mut signature = cir::Signature::new(self.isa.default_call_conv());
cranelift_codegen::ir::Signature::new(self.isa.default_call_conv());
let stack_ret = build_signature( let stack_ret = build_signature(
self.isa.default_call_conv(), self.isa.default_call_conv(),
self.tys.ins.funcs[func].sig, self.tys.ins.funcs[func].sig,
@ -494,29 +529,28 @@ impl FuncBuilder<'_, '_> {
&mut arg_lens, &mut arg_lens,
); );
let func_ref = 'b: { let func_ref =
let user_name_ref = self.bl.func.declare_imported_user_function( 'b: {
cranelift_codegen::ir::UserExternalName { let user_name_ref = self.bl.func.declare_imported_user_function(
namespace: 0, cir::UserExternalName { namespace: 0, index: func.index() as _ },
index: func.index() as _, );
},
);
if let Some(id) = self.bl.func.dfg.ext_funcs.keys().find(|&k| { if let Some(id) = self.bl.func.dfg.ext_funcs.keys().find(|&k| {
self.bl.func.dfg.ext_funcs[k].name self.bl.func.dfg.ext_funcs[k].name
== cranelift_codegen::ir::ExternalName::user(user_name_ref) == cir::ExternalName::user(user_name_ref)
}) { }) {
break 'b id; 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 { self.bl.func.import_function(cir::ExtFuncData {
name: cranelift_codegen::ir::ExternalName::user(user_name_ref), name: cir::ExternalName::user(user_name_ref),
signature, signature,
colocated: true, // somehow, this works
}) colocated: true, // !self.tys.ins.funcs[func].is_import,
}; })
};
let mut ir_args = vec![]; let mut ir_args = vec![];
@ -525,13 +559,15 @@ impl FuncBuilder<'_, '_> {
} }
let mut params = signature.params.as_slice(); let mut params = signature.params.as_slice();
let mut parama_len = let mut parama_len = arg_lens[1..].iter();
arg_lens[(self.tys.size_of(node.ty) != 0) as usize..].iter();
let mut typs = args.args(); let mut typs = args.args();
let mut args = node.inputs[1..].iter(); let mut args = node.inputs[1..].iter();
while let Some(aty) = typs.next(self.tys) { 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(); let abi_meta = parama_len.next().unwrap();
if abi_meta.arg_count == 0 {
continue;
}
let &arg = args.next().unwrap(); let &arg = args.next().unwrap();
if !abi_meta.trough_mem && ty.is_aggregate(self.tys) { if !abi_meta.trough_mem && ty.is_aggregate(self.tys) {
let loc = params.take(..abi_meta.arg_count).unwrap(); let loc = params.take(..abi_meta.arg_count).unwrap();
@ -590,101 +626,74 @@ impl FuncBuilder<'_, '_> {
return; return;
} }
} }
Kind::CInt { value } if self.nodes[nid].ty.is_integer() => Ok(self.bl.ins().iconst( Kind::CInt { value } if self.nodes[nid].ty.is_float() => {
cranelift_codegen::ir::Type::int(self.tys.size_of(self.nodes[nid].ty) as u16 * 8) Ok(match self.tys.size_of(self.nodes[nid].ty) {
.unwrap(), 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, 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 } => { Kind::BinOp { op } => {
let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
let [lhs, rhs] = [self.value_of(lhs), self.value_of(rhs)]; let [lh, rh] = [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)
);
use cranelift_codegen::ir::condcodes::IntCC as ICC; let is_int_op = node.ty.is_integer()
fn icc_of(op: TokenKind, signed: bool) -> ICC { || (node.ty == hbty::Id::BOOL
match op { && (self.nodes[lhs].ty.is_integer()
TokenKind::Lt if signed => ICC::SignedLessThan, || self.nodes[lhs].ty == hbty::Id::BOOL));
TokenKind::Gt if signed => ICC::SignedGreaterThan, let is_float_op = node.ty.is_float()
TokenKind::Le if signed => ICC::SignedLessThanOrEqual, || (node.ty == hbty::Id::BOOL && self.nodes[lhs].ty.is_float());
TokenKind::Ge if signed => ICC::SignedGreaterThanOrEqual,
TokenKind::Lt => ICC::UnsignedLessThan, Ok(if is_int_op {
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() {
let signed = node.ty.is_signed(); let signed = node.ty.is_signed();
match op { match op {
TokenKind::Add => self.bl.ins().iadd(lhs, rhs), TokenKind::Add => self.bl.ins().iadd(lh, rh),
TokenKind::Sub => self.bl.ins().isub(lhs, rhs), TokenKind::Sub => self.bl.ins().isub(lh, rh),
TokenKind::Mul => self.bl.ins().imul(lhs, rhs), TokenKind::Mul => self.bl.ins().imul(lh, rh),
TokenKind::Shl => self.bl.ins().ishl(lhs, rhs), TokenKind::Shl => self.bl.ins().ishl(lh, rh),
TokenKind::Xor => self.bl.ins().bxor(lhs, rhs), TokenKind::Xor => self.bl.ins().bxor(lh, rh),
TokenKind::Band => self.bl.ins().band(lhs, rhs), TokenKind::Band => self.bl.ins().band(lh, rh),
TokenKind::Bor => self.bl.ins().bor(lhs, rhs), TokenKind::Bor => self.bl.ins().bor(lh, rh),
TokenKind::Div if signed => self.bl.ins().sdiv(lhs, rhs), TokenKind::Div if signed => self.bl.ins().sdiv(lh, rh),
TokenKind::Mod if signed => self.bl.ins().srem(lhs, rhs), TokenKind::Mod if signed => self.bl.ins().srem(lh, rh),
TokenKind::Shr if signed => self.bl.ins().sshr(lhs, rhs), TokenKind::Shr if signed => self.bl.ins().sshr(lh, rh),
TokenKind::Div => self.bl.ins().udiv(lhs, rhs), TokenKind::Div => self.bl.ins().udiv(lh, rh),
TokenKind::Mod => self.bl.ins().urem(lhs, rhs), TokenKind::Mod => self.bl.ins().urem(lh, rh),
TokenKind::Shr => self.bl.ins().ushr(lhs, rhs), TokenKind::Shr => self.bl.ins().ushr(lh, rh),
TokenKind::Lt TokenKind::Lt
| TokenKind::Gt | TokenKind::Gt
| TokenKind::Le | TokenKind::Le
| TokenKind::Ge | TokenKind::Ge
| TokenKind::Eq | 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}"), op => todo!("{op}"),
} }
} else if node.ty.is_float() { } else if is_float_op {
match op { match op {
TokenKind::Add => self.bl.ins().fadd(lhs, rhs), TokenKind::Add => self.bl.ins().fadd(lh, rh),
TokenKind::Sub => self.bl.ins().fsub(lhs, rhs), TokenKind::Sub => self.bl.ins().fsub(lh, rh),
TokenKind::Mul => self.bl.ins().fmul(lhs, rhs), TokenKind::Mul => self.bl.ins().fmul(lh, rh),
TokenKind::Div => self.bl.ins().fdiv(lhs, rhs), TokenKind::Div => self.bl.ins().fdiv(lh, rh),
TokenKind::Lt TokenKind::Lt
| TokenKind::Gt | TokenKind::Gt
| TokenKind::Le | TokenKind::Le
| TokenKind::Ge | TokenKind::Ge
| TokenKind::Eq | 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}"), op => todo!("{op}"),
} }
} else { } else {
todo!() todo!("{}", hbty::Display::new(self.tys, self.files, node.ty))
}) })
} }
Kind::RetVal => Ok(self.value_of(node.inputs[0])), Kind::RetVal => Ok(self.value_of(node.inputs[0])),
@ -696,7 +705,7 @@ impl FuncBuilder<'_, '_> {
.inner_of(self.nodes[node.inputs[1]].ty) .inner_of(self.nodes[node.inputs[1]].ty)
.unwrap_or(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 { Ok(match op {
TokenKind::Sub => self.bl.ins().ineg(oper), TokenKind::Sub => self.bl.ins().ineg(oper),
TokenKind::Not => self.bl.ins().bnot(oper), TokenKind::Not => self.bl.ins().bnot(oper),
@ -715,55 +724,52 @@ impl FuncBuilder<'_, '_> {
self.bl.ins().sextend(dty, oper) self.bl.ins().sextend(dty, oper)
} }
TokenKind::Number 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()) => && (dst.is_integer() || dst.is_pointer()) =>
{ {
self.bl.ins().uextend(dty, oper) 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) 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) self.bl.ins().fdemote(dty, oper)
} }
_ => todo!(), _ => todo!(),
}) })
} }
Kind::Stck => { Kind::Stck => {
let slot = self.bl.create_sized_stack_slot(cranelift_codegen::ir::StackSlotData { let slot = self.bl.create_sized_stack_slot(cir::StackSlotData {
kind: cranelift_codegen::ir::StackSlotKind::ExplicitSlot, kind: cir::StackSlotKind::ExplicitSlot,
size: self.tys.size_of(node.ty), size: self.tys.size_of(node.ty),
align_shift: self.tys.align_of(node.ty).ilog2() as _, 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 } => { Kind::Global { global } => {
let glob_ref = { let glob_ref = {
// already deduplicated by the SoN // already deduplicated by the SoN
let colocated = true; let colocated = true;
let user_name_ref = self.bl.func.declare_imported_user_function( let user_name_ref =
cranelift_codegen::ir::UserExternalName { self.bl.func.declare_imported_user_function(cir::UserExternalName {
namespace: 1, namespace: 1,
index: global.index() as u32, index: global.index() as u32,
}, });
); self.bl.func.create_global_value(cir::GlobalValueData::Symbol {
self.bl.func.create_global_value( name: cir::ExternalName::user(user_name_ref),
cranelift_codegen::ir::GlobalValueData::Symbol { offset: cir::immediates::Imm64::new(0),
name: cranelift_codegen::ir::ExternalName::user(user_name_ref), colocated,
offset: cranelift_codegen::ir::immediates::Imm64::new(0), tls: false,
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 if node.ty.is_aggregate(self.tys) => return,
Kind::Load => { Kind::Load => {
let ptr = self.value_of(node.inputs[1]); 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) => { Kind::Stre if node.ty.is_aggregate(self.tys) => {
let src = self.value_of(self.nodes[node.inputs[1]].inputs[1]); 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 { trait ToCondcodes {
if ty.is_integer() { fn to_int_cc(self, signed: bool) -> condcodes::IntCC;
cranelift_codegen::ir::Type::int(tys.size_of(ty) as u16 * 8).unwrap() fn to_float_cc(self) -> condcodes::FloatCC;
} else { }
unimplemented!()
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)] #[derive(Default)]
struct Global { struct Global {
module_id: Option<cranelift_module::DataId>, module_id: Option<cm::DataId>,
} }
#[derive(Default)] #[derive(Default)]
struct FuncHeaders { struct FuncHeaders {
module_id: Option<cranelift_module::FuncId>, module_id: Option<cm::FuncId>,
alignment: u32, alignment: u32,
code: Range<u32>, code: Range<u32>,
relocs: Range<u32>, relocs: Range<u32>,
@ -816,19 +872,14 @@ struct FuncHeaders {
#[derive(Default)] #[derive(Default)]
struct Functions { struct Functions {
headers: EntVec<hblang::ty::Func, FuncHeaders>, headers: EntVec<hbty::Func, FuncHeaders>,
code: Vec<u8>, code: Vec<u8>,
relocs: Vec<FinalizedMachReloc>, relocs: Vec<FinalizedMachReloc>,
external_names: Vec<UserExternalName>, external_names: Vec<UserExternalName>,
} }
impl Functions { impl Functions {
fn push( fn push(&mut self, id: hbty::Func, func: &cir::Function, code: &MachBufferFinalized<Final>) {
&mut self,
id: hblang::ty::Func,
func: &cranelift_codegen::ir::Function,
code: &MachBufferFinalized<Final>,
) {
self.headers.shadow(id.index() + 1); self.headers.shadow(id.index() + 1);
self.headers[id] = FuncHeaders { self.headers[id] = FuncHeaders {
module_id: None, module_id: None,
@ -847,9 +898,9 @@ impl Functions {
#[derive(Default)] #[derive(Default)]
struct Assembler { struct Assembler {
name: String, name: String,
frontier: Vec<hblang::ty::Id>, frontier: Vec<hbty::Id>,
globals: Vec<hblang::ty::Global>, globals: Vec<hbty::Global>,
funcs: Vec<hblang::ty::Func>, funcs: Vec<hbty::Func>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -857,6 +908,7 @@ pub enum BackendCreationError {
UnsupportedTriplet(LookupError), UnsupportedTriplet(LookupError),
InvalidFlags(CodegenError), InvalidFlags(CodegenError),
UnsupportedModuleConfig(ModuleError), UnsupportedModuleConfig(ModuleError),
InvalidFlag { key: String, value: String, err: SetError },
} }
impl Display for BackendCreationError { impl Display for BackendCreationError {
@ -871,6 +923,13 @@ impl Display for BackendCreationError {
BackendCreationError::UnsupportedModuleConfig(err) => { BackendCreationError::UnsupportedModuleConfig(err) => {
write!(f, "Unsupported module configuration: {}", 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())
)
}
} }
} }
} }

View file

@ -1,7 +1,7 @@
// The classification code for the x86_64 ABI is taken from the clay language // The classification code for the x86_64 ABI is taken from the clay language
// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp // https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp
use crate::AbiMeta; use {crate::AbiMeta, hblang::ty};
pub fn build_systemv_signature( pub fn build_systemv_signature(
sig: hblang::ty::Sig, sig: hblang::ty::Sig,
@ -12,11 +12,14 @@ pub fn build_systemv_signature(
let mut alloca = Alloca::new(); let mut alloca = Alloca::new();
alloca.next(false, sig.ret, types, &mut signature.returns); alloca.next(false, sig.ret, types, &mut signature.returns);
let stack_ret = signature.params.len() == 1 let stack_ret = signature.returns.len() == 1
&& signature.params[0].purpose == cranelift_codegen::ir::ArgumentPurpose::StructReturn; && signature.returns[0].purpose == cranelift_codegen::ir::ArgumentPurpose::StructReturn;
if stack_ret { if stack_ret {
signature.params.append(&mut signature.returns); 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(); let mut args = sig.args.args();
@ -66,7 +69,7 @@ fn classify_arg(
let mut c = match layout.expand() { let mut c = match layout.expand() {
_ if size == 0 => return Ok(()), _ 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, _ if layout.is_float() => Class::Sse,
hblang::ty::Kind::Struct(s) => { hblang::ty::Kind::Struct(s) => {
@ -115,7 +118,7 @@ fn classify_arg(
return Ok(()); return Ok(());
} }
_ => unimplemented!(), ty => unimplemented!("{ty:?}"),
}; };
// Fill in `cls` for scalars (Int/Sse) and vectors (Sse). // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
@ -241,6 +244,10 @@ impl Alloca {
cx: &hblang::ty::Types, cx: &hblang::ty::Types,
dest: &mut Vec<cranelift_codegen::ir::AbiParam>, dest: &mut Vec<cranelift_codegen::ir::AbiParam>,
) -> bool { ) -> bool {
if cx.size_of(arg) == 0 {
return false;
}
let mut cls_or_mem = classify_arg(cx, arg); let mut cls_or_mem = classify_arg(cx, arg);
if is_arg { if is_arg {

47
examples/raylib/main.hb Normal file
View file

@ -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
}

4
examples/raylib/run.sh Executable file
View file

@ -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

View file

@ -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 <name> - choose between builtin path resolvers, options are: ableos

View file

@ -106,7 +106,7 @@ pub struct HbvmBackend {
offsets: Vec<Offset>, offsets: Vec<Offset>,
} }
pub const TARGET_TRIPLE: &str = "hbvm-virt-ableos"; pub const TARGET_TRIPLE: &str = "unknown-virt-unknown";
impl HbvmBackend { impl HbvmBackend {
fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) { fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) {

View file

@ -6,7 +6,7 @@ use {
ty, FnvBuildHasher, ty, FnvBuildHasher,
}, },
alloc::{string::String, vec::Vec}, alloc::{string::String, vec::Vec},
core::{fmt::Write, num::NonZeroUsize, ops::Deref}, core::{fmt::Write, ops::Deref},
hashbrown::hash_map, hashbrown::hash_map,
std::{ std::{
borrow::ToOwned, borrow::ToOwned,
@ -74,62 +74,6 @@ pub struct Options<'a> {
pub backend: Option<&'a mut dyn Backend>, pub backend: Option<&'a mut dyn Backend>,
} }
impl<'a> Options<'a> {
pub fn from_args(
args: &[&str],
out: &mut Vec<u8>,
resolvers: &'a [(&str, PathResolver)],
backend: Option<&'a mut dyn Backend>,
) -> std::io::Result<Self> {
if args.contains(&"--help") || args.contains(&"-h") {
writeln!(out, "Usage: hbc [OPTIONS...] <FILE>")?;
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::<NonZeroUsize>().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::<String>()
)
.err()
.unwrap_or(std::io::ErrorKind::Other.into())
},
)
})
.transpose()?,
backend,
})
}
}
pub fn run_compiler( pub fn run_compiler(
root_file: &str, root_file: &str,
options: Options, options: Options,

View file

@ -257,7 +257,11 @@ impl<'a, 'b> Parser<'a, 'b> {
None => { None => {
let ident = match Ident::new(token.start, name.len() as _) { let ident = match Ident::new(token.start, name.len() as _) {
None => { 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() Ident::new(token.start, 63).unwrap()
} }
Some(id) => id, Some(id) => id,

View file

@ -1202,14 +1202,6 @@ impl<'a> Codegen<'a> {
let mut lhs = self.ptr_expr_ctx(left, ctx)?; let mut lhs = self.ptr_expr_ctx(left, ctx)?;
self.implicit_unwrap(left.pos(), &mut lhs); 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() { match lhs.ty.expand() {
ty::Kind::Struct(s) if op.is_homogenous() => { ty::Kind::Struct(s) if op.is_homogenous() => {
debug_assert!(lhs.ptr); debug_assert!(lhs.ptr);
@ -3095,7 +3087,7 @@ impl<'a> Codegen<'a> {
let rhs = self.offset(rhs, off); let rhs = self.offset(rhs, off);
let dst = self.offset(dst, off); let dst = self.offset(dst, off);
match ty.expand() { 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 lhs = self.load_mem(lhs, ty);
let rhs = self.load_mem(rhs, ty); let rhs = self.load_mem(rhs, ty);
let res = let res =
@ -3279,6 +3271,7 @@ impl<'a> Codegen<'a> {
sig: Sig { args, ret }, sig: Sig { args, ret },
is_inline, is_inline,
is_generic: true, is_generic: true,
is_import: false,
comp_state: Default::default(), comp_state: Default::default(),
}) })
.into() .into()
@ -4238,32 +4231,49 @@ impl<'a> Codegen<'a> {
}, },
|s, base| s.ins.unions.push(UnionData { base, ..Default::default() }), |s, base| s.ins.unions.push(UnionData { base, ..Default::default() }),
), ),
Expr::Closure { pos, args, ret, .. } if let Some(name) = sc.name => { Expr::Closure {
let sig = 'b: { pos,
let arg_base = self.tys.tmp.args.len(); args,
for arg in args { ret,
let sym = parser::find_symbol(&self.files[sc.file].symbols, arg.id); body: &Expr::Directive { name: "import", args: import_args, .. },
if sym.flags & idfl::COMPTIME != 0 { } if let Some(name) = sc.name => {
self.tys.tmp.args.truncate(arg_base); let Some(sig) = self.try_parse_concrete_signature(pos, sc, args, ret) else {
break 'b None; return self.error_low(
} sc.file,
let ty = self.parse_ty(sc.anon(), &arg.ty); pos,
if ty == ty::Id::ANY_TYPE { "I am too tired to deal with your bulllshit",
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 })
}; };
//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) => { Some(sig) => {
let func = FuncData { let func = FuncData {
file: sc.file, file: sc.file,
@ -4274,6 +4284,7 @@ impl<'a> Codegen<'a> {
expr: ExprRef::new(expr), expr: ExprRef::new(expr),
is_inline: sc.is_ct, is_inline: sc.is_ct,
is_generic: false, is_generic: false,
is_import: false,
comp_state: Default::default(), comp_state: Default::default(),
}; };
self.tys.ins.funcs.push(func).into() 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<Sig> {
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)] #[expect(clippy::too_many_arguments)]
fn parse_base_ty<A, F, T: Into<ty::Id>>( fn parse_base_ty<A, F, T: Into<ty::Id>>(
&mut self, &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)] #[cfg(test)]
mod tests { mod tests {
use { use {

View file

@ -671,6 +671,7 @@ pub struct FuncData {
pub sig: Sig, pub sig: Sig,
pub is_inline: bool, pub is_inline: bool,
pub is_generic: bool, pub is_generic: bool,
pub is_import: bool,
pub comp_state: [PackedCompState; 2], pub comp_state: [PackedCompState; 2],
} }
@ -928,6 +929,7 @@ impl Types {
| Kind::Slice(_) | Kind::Slice(_)
| Kind::Tuple(_) | Kind::Tuple(_)
| Kind::Opt(_) => utils::is_pascal_case, | Kind::Opt(_) => utils::is_pascal_case,
Kind::Func(f) if self.ins.funcs[f].is_import => |_| Ok(()),
Kind::Func(f) Kind::Func(f)
if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } = if let &Expr::Closure { ret: &Expr::Ident { id, .. }, .. } =
self.ins.funcs[f].expr.get(&files[self.ins.funcs[f].file]) 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::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::Func(_)
| Kind::Template(_) | Kind::Template(_)
| Kind::Global(_) | Kind::Global(_)

15
smh.hb
View file

@ -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
}