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
out.o
/examples/raylib/main
# 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"
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"

View file

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

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() {
use std::io::Write;
fn run(out: &mut Vec<u8>, warnings: &mut String) -> std::io::Result<()> {
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
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);
}
}

View file

@ -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<cranelift_object::ObjectModule>,
ctrl_plane: cranelift_codegen::control::ControlPlane,
ctrl_plane: cc::control::ControlPlane,
funcs: Functions,
globals: EntVec<hblang::ty::Global, Global>,
globals: EntVec<hbty::Global, Global>,
asm: Assembler,
}
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 {
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<hblang::ty::Module, hblang::parser::Ast>,
from: hbty::Func,
types: &hbty::Types,
files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
to: &mut Vec<u8>,
) -> 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<hblang::ty::Module, hblang::parser::Ast>,
_types: &'a hbty::Types,
_files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
_output: &mut String,
) -> Result<(), std::boxed::Box<dyn core::error::Error + Send + Sync + 'a>> {
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<hblang::ty::Module, hblang::parser::Ast>,
id: hbty::Func,
nodes: &hbnodes::Nodes,
tys: &hbty::Types,
files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
) {
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<AbiMeta>,
) -> 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<hblang::ty::Module, hblang::parser::Ast>,
values: &'b mut [Option<Result<cranelift_codegen::ir::Value, cranelift_codegen::ir::Block>>],
nodes: &'a hbnodes::Nodes,
tys: &'a hbty::Types,
files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
values: &'b mut [Option<Result<cir::Value, cir::Block>>],
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<cranelift_module::DataId>,
module_id: Option<cm::DataId>,
}
#[derive(Default)]
struct FuncHeaders {
module_id: Option<cranelift_module::FuncId>,
module_id: Option<cm::FuncId>,
alignment: u32,
code: Range<u32>,
relocs: Range<u32>,
@ -816,19 +872,14 @@ struct FuncHeaders {
#[derive(Default)]
struct Functions {
headers: EntVec<hblang::ty::Func, FuncHeaders>,
headers: EntVec<hbty::Func, FuncHeaders>,
code: Vec<u8>,
relocs: Vec<FinalizedMachReloc>,
external_names: Vec<UserExternalName>,
}
impl Functions {
fn push(
&mut self,
id: hblang::ty::Func,
func: &cranelift_codegen::ir::Function,
code: &MachBufferFinalized<Final>,
) {
fn push(&mut self, id: hbty::Func, func: &cir::Function, code: &MachBufferFinalized<Final>) {
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<hblang::ty::Id>,
globals: Vec<hblang::ty::Global>,
funcs: Vec<hblang::ty::Func>,
frontier: Vec<hbty::Id>,
globals: Vec<hbty::Global>,
funcs: Vec<hbty::Func>,
}
#[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())
)
}
}
}
}

View file

@ -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<cranelift_codegen::ir::AbiParam>,
) -> bool {
if cx.size_of(arg) == 0 {
return false;
}
let mut cls_or_mem = classify_arg(cx, 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>,
}
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])) {

View file

@ -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<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(
root_file: &str,
options: Options,

View file

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

View file

@ -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<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)]
fn parse_base_ty<A, F, T: Into<ty::Id>>(
&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 {

View file

@ -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(_)

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
}