adding cranelift backend

also splitting hbc binary into a separate crate to avoid dependency
cycle, the backand is thus far capable of compiling the simplest program
that returns an custom status code

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-29 13:30:29 +01:00
parent 08fc9d6ab6
commit ee67ebb017
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
18 changed files with 1196 additions and 56 deletions

3
.gitignore vendored
View file

@ -2,6 +2,9 @@
/target
rustc-ice-*
a.out
out.o
# sqlite
db.sqlite
db.sqlite-journal

231
Cargo.lock generated
View file

@ -39,10 +39,22 @@ dependencies = [
]
[[package]]
name = "anyhow"
version = "1.0.89"
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anyhow"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "arbitrary"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "arc-swap"
@ -229,7 +241,7 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"rustc-hash 1.1.0",
"shlex",
"syn",
"which",
@ -259,6 +271,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
dependencies = [
"allocator-api2",
]
[[package]]
name = "bytes"
version = "1.8.0"
@ -340,6 +361,143 @@ dependencies = [
"libc",
]
[[package]]
name = "cranelift-backend"
version = "0.1.0"
dependencies = [
"cranelift-codegen",
"cranelift-frontend",
"cranelift-module",
"cranelift-object",
"hblang",
"target-lexicon",
]
[[package]]
name = "cranelift-bforest"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac89549be94911dd0e839b4a7db99e9ed29c17517e1c026f61066884c168aa3c"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-bitset"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9bd49369f76c77e34e641af85d0956869237832c118964d08bf5f51f210875a"
[[package]]
name = "cranelift-codegen"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd96ce9cf8efebd7f5ab8ced5a0ce44250280bbae9f593d74a6d7effc3582a35"
dependencies = [
"bumpalo",
"cranelift-bforest",
"cranelift-bitset",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-control",
"cranelift-entity",
"cranelift-isle",
"gimli 0.31.1",
"hashbrown 0.14.5",
"log",
"regalloc2",
"rustc-hash 2.1.0",
"serde",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a68e358827afe4bfb6239fcbf6fbd5ac56206ece8a99c8f5f9bbd518773281a"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e184c9767afbe73d50c55ec29abcf4c32f9baf0d9d22b86d58c4d55e06dee181"
[[package]]
name = "cranelift-control"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc7664f2a66f053e33f149e952bb5971d138e3af637f5097727ed6dc0ed95dd"
dependencies = [
"arbitrary",
]
[[package]]
name = "cranelift-entity"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "118597e3a9cf86c3556fa579a7a23b955fa18231651a52a77a2475d305a9cf84"
dependencies = [
"cranelift-bitset",
]
[[package]]
name = "cranelift-frontend"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7638ea1efb069a0aa18d8ee67401b6b0d19f6bfe5de5e9ede348bfc80bb0d8c7"
dependencies = [
"cranelift-codegen",
"log",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-isle"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c53e1152a0b01c4ed2b1e0535602b8e86458777dd9d18b28732b16325c7dc0"
[[package]]
name = "cranelift-module"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11841b3f54ac480db1e8e8d5678ba901a13b387012d315e3f8fba3e7b7a80447"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-control",
]
[[package]]
name = "cranelift-object"
version = "0.115.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e235ddfd19f100855ad03358c7ae0a13070c38a000701054cab46458cca6e81"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-control",
"cranelift-module",
"log",
"object",
"target-lexicon",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -441,6 +599,12 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@ -532,6 +696,11 @@ name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
dependencies = [
"fallible-iterator 0.3.0",
"indexmap 2.6.0",
"stable_deref_trait",
]
[[package]]
name = "glob"
@ -579,6 +748,9 @@ name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
dependencies = [
"foldhash",
]
[[package]]
name = "hashlink"
@ -593,6 +765,16 @@ dependencies = [
name = "hbbytecode"
version = "0.1.0"
[[package]]
name = "hbc"
version = "0.1.0"
dependencies = [
"cranelift-backend",
"hblang",
"log",
"target-lexicon",
]
[[package]]
name = "hblang"
version = "0.1.0"
@ -935,6 +1117,9 @@ version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
dependencies = [
"crc32fast",
"hashbrown 0.15.0",
"indexmap 2.6.0",
"memchr",
]
@ -1048,6 +1233,20 @@ dependencies = [
"getrandom",
]
[[package]]
name = "regalloc2"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3"
dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.15.0",
"log",
"rustc-hash 2.1.0",
"smallvec",
]
[[package]]
name = "regex"
version = "1.11.1"
@ -1118,6 +1317,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
[[package]]
name = "rustix"
version = "0.38.37"
@ -1192,18 +1397,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
@ -1295,9 +1500,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.79"
version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [
"proc-macro2",
"quote",
@ -1316,6 +1521,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "time"
version = "0.3.36"

View file

@ -12,6 +12,8 @@ members = [
"depell/wasm-fmt",
"depell/wasm-hbc",
"depell/wasm-rt",
"cranelift-backend",
"c",
]
[workspace.dependencies]

10
c/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "hbc"
version = "0.1.0"
edition = "2024"
[dependencies]
cranelift-backend = { version = "0.1.0", path = "../cranelift-backend" }
hblang = { workspace = true, features = ["std"] }
log = "0.4.22"
target-lexicon = { version = "0.12", features = ["std"] }

View file

@ -1,4 +1,5 @@
#[cfg(feature = "std")]
use std::io;
fn main() {
use std::io::Write;
@ -6,7 +7,22 @@ fn main() {
let args = std::env::args().collect::<Vec<_>>();
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
let resolvers = &[("ableos", hblang::ABLEOS_PATH_RESOLVER)];
let opts = hblang::Options::from_args(&args, out, resolvers)?;
let mut native = None;
let backend = match args.iter().position(|&v| v == "--target").map(|i| args[i + 1]) {
Some(hblang::backend::hbvm::TARGET_TRIPLE) => None,
Some(target) => Some(
native.insert(
cranelift_backend::Backend::new(target.parse().map_err(io::Error::other)?)
.map_err(io::Error::other)?,
) as &mut dyn hblang::backend::Backend,
),
None => Some(native.insert(
cranelift_backend::Backend::new(target_lexicon::HOST).map_err(io::Error::other)?,
) as &mut dyn hblang::backend::Backend),
};
let opts = hblang::Options::from_args(&args, out, resolvers, backend)?;
let file = args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb");
hblang::run_compiler(file, opts, out, warnings)

View file

@ -0,0 +1,12 @@
[package]
name = "cranelift-backend"
version = "0.1.0"
edition = "2024"
[dependencies]
cranelift-codegen = "0.115.0"
cranelift-frontend = "0.115.0"
cranelift-module = "0.115.0"
cranelift-object = "0.115.0"
hblang.workspace = true
target-lexicon = "0.12"

View file

@ -0,0 +1,534 @@
#![feature(if_let_guard)]
#![feature(slice_take)]
use {
cranelift_codegen::{
CodegenError, Final, FinalizedMachReloc, MachBufferFinalized,
ir::{InstBuilder, UserExternalName},
isa::LookupError,
settings::Configurable,
},
cranelift_frontend::FunctionBuilder,
cranelift_module::{Module, ModuleError},
hblang::{
nodes::Kind,
utils::{Ent, EntVec},
},
std::{fmt::Display, ops::Range},
};
mod x86_64;
pub struct Backend {
ctx: cranelift_codegen::Context,
dt_ctx: cranelift_module::DataDescription,
fb_ctx: cranelift_frontend::FunctionBuilderContext,
module: Option<cranelift_object::ObjectModule>,
ctrl_plane: cranelift_codegen::control::ControlPlane,
funcs: Functions,
globals: EntVec<hblang::ty::Global, Global>,
asm: Assembler,
}
impl Backend {
pub fn new(triple: target_lexicon::Triple) -> 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(),
module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new(
cranelift_codegen::isa::lookup(triple)?.finish(
cranelift_codegen::settings::Flags::new({
let mut bl = cranelift_codegen::settings::builder();
bl.set("enable_verifier", "true").unwrap();
bl
}),
)?,
"main",
cranelift_module::default_libcall_names(),
)?)
.into(),
funcs: Default::default(),
globals: Default::default(),
asm: Default::default(),
})
}
}
impl hblang::backend::Backend for Backend {
fn assemble_reachable(
&mut self,
from: hblang::ty::Func,
types: &hblang::ty::Types,
files: &hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>,
to: &mut Vec<u8>,
) -> hblang::backend::AssemblySpec {
debug_assert!(self.asm.frontier.is_empty());
debug_assert!(self.asm.funcs.is_empty());
debug_assert!(self.asm.globals.is_empty());
let mut module = self.module.take().expect("backend can assemble only once");
fn clif_name_to_ty(name: UserExternalName) -> hblang::ty::Id {
match name.namespace {
0 => hblang::ty::Kind::Func(hblang::ty::Func::new(name.index as _)),
1 => hblang::ty::Kind::Global(hblang::ty::Global::new(name.index as _)),
_ => unreachable!(),
}
.compress()
}
self.globals.shadow(types.ins.globals.len());
self.asm.frontier.push(from.into());
while let Some(itm) = self.asm.frontier.pop() {
match itm.expand() {
hblang::ty::Kind::Func(func) => {
let fuc = &mut self.funcs.headers[func];
self.asm.funcs.push(func);
self.asm.frontier.extend(
fuc.external_names.clone().map(|r| {
clif_name_to_ty(self.funcs.external_names[r as usize].clone())
}),
);
self.asm.name.clear();
if func == from {
self.asm.name.push_str("main");
} else {
let file = &files[types.ins.funcs[func].file];
self.asm.name.push_str(&file.path);
self.asm.name.push('.');
self.asm.name.push_str(file.ident_str(types.ins.funcs[func].name));
}
let linkage = if func == from {
cranelift_module::Linkage::Export
} else {
cranelift_module::Linkage::Local
};
build_signature(
module.isa().default_call_conv(),
types.ins.funcs[func].sig,
types,
&mut self.ctx.func.signature,
&mut vec![],
);
fuc.module_id = Some(
module
.declare_function(&self.asm.name, linkage, &self.ctx.func.signature)
.unwrap(),
);
}
hblang::ty::Kind::Global(glob) => {
self.asm.globals.push(glob);
self.asm.name.clear();
let file = &files[types.ins.globals[glob].file];
self.asm.name.push_str(&file.path);
self.asm.name.push('.');
self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name));
self.globals[glob].module_id = Some(
module
.declare_data(
&self.asm.name,
cranelift_module::Linkage::Local,
true,
false,
)
.unwrap(),
);
}
_ => unreachable!(),
}
}
for &func in &self.asm.funcs {
let fuc = &self.funcs.headers[func];
debug_assert!(!fuc.code.is_empty());
let names = &mut self.funcs.external_names
[fuc.external_names.start as usize..fuc.external_names.end as usize];
names.iter_mut().for_each(|nm| {
nm.index = fuc.module_id.unwrap().as_u32();
self.ctx.func.params.ensure_user_func_name(nm.clone());
});
module
.define_function_bytes(
fuc.module_id.unwrap(),
&self.ctx.func,
fuc.alignment as _,
&self.funcs.code[fuc.code.start as usize..fuc.code.end as usize],
&self.funcs.relocs[fuc.relocs.start as usize..fuc.relocs.end as usize],
)
.unwrap();
}
for global in self.asm.globals.drain(..) {
let glob = &self.globals[global];
self.dt_ctx.clear();
self.dt_ctx.define(types.ins.globals[global].data.clone().into());
module.define_data(glob.module_id.unwrap(), &self.dt_ctx).unwrap();
}
module.finish().object.write_stream(to).unwrap();
hblang::backend::AssemblySpec { code_length: 0, data_length: 0, entry: 0 }
}
fn disasm<'a>(
&'a self,
_sluce: &[u8],
_eca_handler: &mut dyn FnMut(&mut &[u8]),
_types: &'a hblang::ty::Types,
_files: &'a hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>,
_output: &mut String,
) -> Result<(), std::boxed::Box<dyn core::error::Error + Send + Sync + 'a>> {
unimplemented!()
}
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>,
) {
self.ctx.clear();
let mut lens = vec![];
let stack_ret = build_signature(
self.module.as_ref().unwrap().isa().default_call_conv(),
tys.ins.funcs[id].sig,
tys,
&mut self.ctx.func.signature,
&mut lens,
);
FuncBuilder {
bl: FunctionBuilder::new(&mut self.ctx.func, &mut self.fb_ctx),
nodes,
tys,
files,
values: &mut vec![None; nodes.len()],
}
.build(tys.ins.funcs[id].sig, &lens, stack_ret);
self.ctx.compile(self.module.as_ref().unwrap().isa(), &mut self.ctrl_plane).unwrap();
let code = self.ctx.compiled_code().unwrap();
self.funcs.push(id, &self.ctx.func, &code.buffer);
}
}
fn build_signature(
call_conv: cranelift_codegen::isa::CallConv,
sig: hblang::ty::Sig,
types: &hblang::ty::Types,
signature: &mut cranelift_codegen::ir::Signature,
arg_lens: &mut Vec<usize>,
) -> bool {
signature.clear(call_conv);
match call_conv {
cranelift_codegen::isa::CallConv::SystemV => {
x86_64::build_systemv_signature(sig, types, signature, arg_lens)
}
_ => todo!(),
}
}
struct FuncBuilder<'a, 'b> {
bl: cranelift_frontend::FunctionBuilder<'b>,
nodes: &'a hblang::nodes::Nodes,
tys: &'a hblang::ty::Types,
#[expect(unused)]
files: &'a hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>,
values: &'b mut [Option<Result<cranelift_codegen::ir::Value, cranelift_codegen::ir::Block>>],
}
impl FuncBuilder<'_, '_> {
pub fn build(mut self, sig: hblang::ty::Sig, arg_lens: &[usize], stack_ret: bool) {
let entry = self.bl.create_block();
self.bl.append_block_params_for_function_params(entry);
self.bl.switch_to_block(entry);
let mut arg_vals = self.bl.block_params(entry);
if stack_ret {
let ret_ptr = *arg_vals.take_first().unwrap();
self.values[hblang::nodes::MEM as usize] = Some(Ok(ret_ptr));
}
let Self { nodes, tys, .. } = self;
let mut parama_len = arg_lens.iter();
let mut typs = sig.args.args();
let mut args = nodes[hblang::nodes::VOID].outputs[hblang::nodes::ARG_START..].iter();
while let Some(aty) = typs.next(tys) {
let hblang::ty::Arg::Value(ty) = aty else { continue };
let loc = arg_vals.take(..*parama_len.next().unwrap()).unwrap();
let &arg = args.next().unwrap();
if ty.is_aggregate(tys) {
todo!()
} else {
debug_assert_eq!(loc.len(), 0);
self.values[arg as usize] = Some(Ok(loc[0]));
}
}
self.values[hblang::nodes::ENTRY as usize] = Some(Err(entry));
self.emit_node(hblang::nodes::VOID, hblang::nodes::VOID);
self.bl.finalize();
}
fn value_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Value {
self.values[nid as usize].unwrap().unwrap()
}
fn block_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Block {
self.values[nid as usize].unwrap().unwrap_err()
}
fn close_block(&mut self, nid: hblang::nodes::Nid) {
if matches!(self.nodes[nid].kind, Kind::Loop | Kind::Region) {
return;
}
self.bl.seal_block(self.block_of(nid));
}
fn emit_node(&mut self, nid: hblang::nodes::Nid, block: hblang::nodes::Nid) {
use hblang::nodes::*;
let mut args = vec![];
if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) {
let side = 1 + self.values[nid as usize].is_some() as usize;
for &o in self.nodes[nid].outputs.iter() {
if self.nodes[o].is_data_phi() {
args.push(self.value_of(self.nodes[0].inputs[side]));
}
}
match (self.nodes[nid].kind, self.values[nid as usize]) {
(Kind::Loop, Some(blck)) => {
self.bl.ins().jump(blck.unwrap_err(), &args);
self.bl.seal_block(blck.unwrap_err());
return;
}
(Kind::Region, None) => {
let next = self.bl.create_block();
for &o in self.nodes[nid].outputs.iter() {
if self.nodes[o].is_data_phi() {
self.values[o as usize] = Some(Ok(self.bl.append_block_param(
next,
ty_to_clif_ty(self.nodes[o].ty, self.tys),
)));
}
}
self.bl.ins().jump(next, &args);
self.bl.seal_block(next);
self.values[nid as usize] = Some(Err(next));
return;
}
_ => {}
}
}
let node = &self.nodes[nid];
self.values[nid as usize] = Some(match node.kind {
Kind::Start => {
debug_assert_eq!(self.nodes[node.outputs[0]].kind, Kind::Entry);
self.emit_node(node.outputs[0], block);
return;
}
Kind::If => {
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
let &[then, else_] = node.outputs.as_slice() else { unreachable!() };
let then_bl = self.bl.create_block();
let else_bl = self.bl.create_block();
let c = self.value_of(cnd);
self.bl.ins().brif(c, then_bl, &[], else_bl, &[]);
self.values[then as usize] = Some(Err(then_bl));
self.values[else_ as usize] = Some(Err(else_bl));
self.close_block(block);
self.bl.switch_to_block(then_bl);
self.emit_node(then, then);
self.bl.switch_to_block(else_bl);
self.emit_node(else_, else_);
Err(self.block_of(block))
}
Kind::Region | Kind::Loop => {
if node.kind == Kind::Loop {
let next = self.bl.create_block();
for &o in self.nodes[nid].outputs.iter() {
if self.nodes[o].is_data_phi() {
self.values[o as usize] = Some(Ok(self.bl.append_block_param(
next,
ty_to_clif_ty(self.nodes[o].ty, self.tys),
)));
}
}
self.values[nid as usize] = Some(Err(next));
}
self.bl.ins().jump(self.values[nid as usize].unwrap().unwrap_err(), &args);
self.close_block(block);
self.bl.switch_to_block(self.values[nid as usize].unwrap().unwrap_err());
for &o in node.outputs.iter().rev() {
self.emit_node(o, nid);
}
Err(self.block_of(block))
}
Kind::Return { .. } | Kind::Die => {
let ret = self.value_of(node.inputs[1]);
self.bl.ins().return_(&[ret]);
self.close_block(block);
self.emit_node(node.outputs[0], block);
Err(self.block_of(block))
}
Kind::Entry => {
for &o in node.outputs.iter().rev() {
self.emit_node(o, nid);
}
return;
}
Kind::Then | Kind::Else => {
for &o in node.outputs.iter().rev() {
self.emit_node(o, block);
}
Err(self.block_of(block))
}
Kind::Call { func: _, unreachable, .. } => {
if unreachable {
todo!()
} else {
todo!();
//for &o in node.outputs.iter().rev() {
// if self.nodes[o].inputs[0] == nid
// || (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region)
// && self.nodes[o].inputs[1] == nid)
// {
// self.emit_node(o, block);
// }
//}
}
}
Kind::CInt { value } if self.nodes[nid].ty.is_integer() => Ok(self.bl.ins().iconst(
cranelift_codegen::ir::Type::int(self.tys.size_of(self.nodes[nid].ty) as u16 * 8)
.unwrap(),
value,
)),
Kind::CInt { value } => Ok(match self.tys.size_of(self.nodes[nid].ty) {
4 => self.bl.ins().f32const(f64::from_bits(value as _) as f32),
8 => self.bl.ins().f64const(f64::from_bits(value as _)),
_ => unimplemented!(),
}),
Kind::BinOp { .. }
| Kind::UnOp { .. }
| Kind::Global { .. }
| Kind::Load { .. }
| Kind::Stre
| Kind::RetVal
| Kind::Stck => todo!(),
Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops | Kind::Join => return,
Kind::Assert { .. } => unreachable!(),
});
}
}
fn ty_to_clif_ty(ty: hblang::ty::Id, tys: &hblang::ty::Types) -> cranelift_codegen::ir::Type {
if ty.is_integer() {
cranelift_codegen::ir::Type::int(tys.size_of(ty) as u16 * 8).unwrap()
} else {
unimplemented!()
}
}
#[derive(Default)]
struct Global {
module_id: Option<cranelift_module::DataId>,
}
#[derive(Default)]
struct FuncHeaders {
module_id: Option<cranelift_module::FuncId>,
alignment: u32,
code: Range<u32>,
relocs: Range<u32>,
external_names: Range<u32>,
}
#[derive(Default)]
struct Functions {
headers: EntVec<hblang::ty::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>,
) {
self.headers.shadow(id.index() + 1);
self.headers[id] = FuncHeaders {
module_id: None,
alignment: code.alignment,
code: self.code.len() as u32..self.code.len() as u32 + code.data().len() as u32,
relocs: self.relocs.len() as u32..self.relocs.len() as u32 + code.relocs().len() as u32,
external_names: self.external_names.len() as u32
..self.external_names.len() as u32 + func.params.user_named_funcs().len() as u32,
};
self.code.extend(code.data());
self.relocs.extend(code.relocs().iter().cloned());
self.external_names.extend(func.params.user_named_funcs().values().cloned());
}
}
#[derive(Default)]
struct Assembler {
name: String,
frontier: Vec<hblang::ty::Id>,
globals: Vec<hblang::ty::Global>,
funcs: Vec<hblang::ty::Func>,
}
#[derive(Debug)]
pub enum BackendCreationError {
UnsupportedTriplet(LookupError),
InvalidFlags(CodegenError),
UnsupportedModuleConfig(ModuleError),
}
impl Display for BackendCreationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
BackendCreationError::UnsupportedTriplet(err) => {
write!(f, "Unsupported triplet: {}", err)
}
BackendCreationError::InvalidFlags(err) => {
write!(f, "Invalid flags: {}", err)
}
BackendCreationError::UnsupportedModuleConfig(err) => {
write!(f, "Unsupported module configuration: {}", err)
}
}
}
}
impl core::error::Error for BackendCreationError {}
impl From<LookupError> for BackendCreationError {
fn from(value: LookupError) -> Self {
Self::UnsupportedTriplet(value)
}
}
impl From<CodegenError> for BackendCreationError {
fn from(value: CodegenError) -> Self {
Self::InvalidFlags(value)
}
}
impl From<ModuleError> for BackendCreationError {
fn from(value: ModuleError) -> Self {
Self::UnsupportedModuleConfig(value)
}
}

View file

@ -0,0 +1,299 @@
// The classification code for the x86_64 ABI is taken from the clay language
// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp
pub fn build_systemv_signature(
sig: hblang::ty::Sig,
types: &hblang::ty::Types,
signature: &mut cranelift_codegen::ir::Signature,
arg_lens: &mut Vec<usize>,
) -> bool {
let mut alloca = Alloca::new();
alloca.next(false, sig.ret, types, &mut signature.returns);
let stack_ret = signature.params.len() == 1
&& signature.params[0].purpose == cranelift_codegen::ir::ArgumentPurpose::StructReturn;
if stack_ret {
signature.params.append(&mut signature.returns);
}
let mut args = sig.args.args();
while let Some(arg) = args.next_value(types) {
let prev = signature.params.len();
alloca.next(true, arg, types, &mut signature.params);
arg_lens.push(signature.params.len() - prev);
}
stack_ret
}
/// Classification of "eightbyte" components.
// N.B., the order of the variants is from general to specific,
// such that `unify(a, b)` is the "smaller" of `a` and `b`.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
enum Class {
Int,
Sse,
SseUp,
}
#[derive(Clone, Copy, Debug)]
struct Memory;
// Currently supported vector size (AVX-512).
const LARGEST_VECTOR_SIZE: usize = 512;
const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64;
fn classify_arg(
cx: &hblang::ty::Types,
arg: hblang::ty::Id,
) -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory> {
fn classify(
cx: &hblang::ty::Types,
layout: hblang::ty::Id,
cls: &mut [Option<Class>],
off: hblang::ty::Offset,
) -> Result<(), Memory> {
let size = cx.size_of(layout);
if off & (cx.align_of(layout) - 1) != 0 {
if size != 0 {
return Err(Memory);
}
return Ok(());
}
let mut c = match layout.expand() {
_ if size == 0 => return Ok(()),
_ if layout.is_integer() || layout.is_pointer() => Class::Int,
_ if layout.is_float() => Class::Sse,
hblang::ty::Kind::Struct(s) => {
for (f, foff) in hblang::ty::OffsetIter::new(s, cx).into_iter(cx) {
classify(cx, f.ty, cls, off + foff)?;
}
return Ok(());
}
hblang::ty::Kind::Tuple(tuple) => {
for (&ty, foff) in hblang::ty::OffsetIter::new(tuple, cx).into_iter(cx) {
classify(cx, ty, cls, off + foff)?;
}
return Ok(());
}
hblang::ty::Kind::Enum(_) => Class::Int,
hblang::ty::Kind::Union(union) => {
for f in cx.union_fields(union) {
classify(cx, f.ty, cls, off)?;
}
return Ok(());
}
hblang::ty::Kind::Slice(slice) if let Some(len) = cx.ins.slices[slice].len() => {
for i in 0..len as u32 {
classify(
cx,
cx.ins.slices[slice].elem,
cls,
off + i * cx.size_of(cx.ins.slices[slice].elem),
)?;
}
return Ok(());
}
hblang::ty::Kind::Slice(_) => {
classify(cx, hblang::ty::Id::UINT, cls, off)?;
classify(cx, hblang::ty::Id::UINT, cls, off + 8)?;
return Ok(());
}
hblang::ty::Kind::Opt(opt) => {
let base = cx.ins.opts[opt].base;
if cx.nieche_of(base).is_some() {
classify(cx, base, cls, off)?;
} else {
classify(cx, hblang::ty::Id::BOOL, cls, off)?;
classify(cx, base, cls, off + cx.align_of(base))?;
}
return Ok(());
}
_ => unimplemented!(),
};
// Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
let first = (off / 8) as usize;
let last = ((off + size - 1) / 8) as usize;
for cls in &mut cls[first..=last] {
*cls = Some(cls.map_or(c, |old| old.min(c)));
// Everything after the first Sse "eightbyte"
// component is the upper half of a register.
if c == Class::Sse {
c = Class::SseUp;
}
}
Ok(())
}
let size = cx.size_of(arg);
let n = ((size + 7) / 8) as usize;
if n > MAX_EIGHTBYTES {
return Err(Memory);
}
let mut cls = [None; MAX_EIGHTBYTES];
classify(cx, arg, &mut cls, 0)?;
if n > 2 {
if cls[0] != Some(Class::Sse) {
return Err(Memory);
}
if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) {
return Err(Memory);
}
} else {
let mut i = 0;
while i < n {
if cls[i] == Some(Class::SseUp) {
cls[i] = Some(Class::Sse);
} else if cls[i] == Some(Class::Sse) {
i += 1;
while i != n && cls[i] == Some(Class::SseUp) {
i += 1;
}
} else {
i += 1;
}
}
}
Ok(cls)
}
fn reg_component(
cls: &[Option<Class>],
i: &mut usize,
size: hblang::ty::Size,
) -> Option<cranelift_codegen::ir::Type> {
if *i >= cls.len() {
return None;
}
match cls[*i] {
None => None,
Some(Class::Int) => {
*i += 1;
Some(if size < 8 {
cranelift_codegen::ir::Type::int(size as u16 * 8).unwrap()
} else {
cranelift_codegen::ir::types::I64
})
}
Some(Class::Sse) => {
let vec_len =
1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count();
*i += vec_len;
Some(if vec_len == 1 {
match size {
4 => cranelift_codegen::ir::types::F32,
_ => cranelift_codegen::ir::types::F64,
}
} else {
cranelift_codegen::ir::types::I64.by(vec_len as _).unwrap()
})
}
Some(c) => unreachable!("reg_component: unhandled class {:?}", c),
}
}
fn cast_target(
cls: &[Option<Class>],
size: hblang::ty::Size,
dest: &mut Vec<cranelift_codegen::ir::AbiParam>,
) {
let mut i = 0;
let lo = reg_component(cls, &mut i, size).unwrap();
let offset = 8 * (i as u32);
dest.push(cranelift_codegen::ir::AbiParam::new(lo));
if size > offset {
if let Some(hi) = reg_component(cls, &mut i, size - offset) {
dest.push(cranelift_codegen::ir::AbiParam::new(hi));
}
}
assert_eq!(reg_component(cls, &mut i, 0), None);
}
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
const MAX_SSE_REGS: usize = 8; // XMM0-7
pub struct Alloca {
int_regs: usize,
sse_regs: usize,
}
impl Alloca {
pub fn new() -> Self {
Self { int_regs: MAX_INT_REGS, sse_regs: MAX_SSE_REGS }
}
pub fn next(
&mut self,
is_arg: bool,
arg: hblang::ty::Id,
cx: &hblang::ty::Types,
dest: &mut Vec<cranelift_codegen::ir::AbiParam>,
) {
let mut cls_or_mem = classify_arg(cx, arg);
if is_arg {
if let Ok(cls) = cls_or_mem {
let mut needed_int = 0;
let mut needed_sse = 0;
for c in cls {
match c {
Some(Class::Int) => needed_int += 1,
Some(Class::Sse) => needed_sse += 1,
_ => {}
}
}
match (self.int_regs.checked_sub(needed_int), self.sse_regs.checked_sub(needed_sse))
{
(Some(left_int), Some(left_sse)) => {
self.int_regs = left_int;
self.sse_regs = left_sse;
}
_ => {
// Not enough registers for this argument, so it will be
// passed on the stack, but we only mark aggregates
// explicitly as indirect `byval` arguments, as LLVM will
// automatically put immediates on the stack itself.
if arg.is_aggregate(cx) {
cls_or_mem = Err(Memory);
}
}
}
}
}
match cls_or_mem {
Err(Memory) => {
if is_arg {
dest.push(cranelift_codegen::ir::AbiParam::new(
cranelift_codegen::ir::types::I64,
));
} else {
dest.push(cranelift_codegen::ir::AbiParam::special(
cranelift_codegen::ir::types::I64,
cranelift_codegen::ir::ArgumentPurpose::StructReturn,
));
}
}
Ok(ref cls) => {
// split into sized chunks passed individually
if arg.is_aggregate(cx) {
cast_target(cls, cx.size_of(arg), dest);
} else {
dest.push(cranelift_codegen::ir::AbiParam::new(
reg_component(cls, &mut 0, cx.size_of(arg)).unwrap(),
));
}
}
}
}
}

View file

@ -9,7 +9,7 @@ use {
backend::hbvm::HbvmBackend,
son::{Codegen, CodegenCtx},
ty::Module,
Ent,
utils::Ent,
},
};

View file

@ -3,10 +3,6 @@ name = "hblang"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "hbc"
path = "src/main.rs"
[[bin]]
name = "fuzz"
path = "src/fuzz_main.rs"

View file

@ -8,7 +8,7 @@ use {
utils::{EntSlice, EntVec},
},
alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec},
core::{assert_matches::debug_assert_matches, mem, ops::Range},
core::{assert_matches::debug_assert_matches, error, mem, ops::Range},
hbbytecode::{self as instrs, *},
reg::Reg,
};
@ -106,6 +106,8 @@ pub struct HbvmBackend {
offsets: Vec<Offset>,
}
pub const TARGET_TRIPLE: &str = "hbvm-virt-ableos";
impl HbvmBackend {
fn emit(&mut self, instr: (usize, [u8; instrs::MAX_SIZE])) {
emit(&mut self.code, instr);
@ -113,12 +115,18 @@ impl HbvmBackend {
}
impl Backend for HbvmBackend {
fn assemble_bin(&mut self, entry: ty::Func, types: &Types, to: &mut Vec<u8>) {
fn assemble_bin(
&mut self,
entry: ty::Func,
types: &Types,
files: &EntSlice<Module, parser::Ast>,
to: &mut Vec<u8>,
) {
to.extend([0u8; HEADER_SIZE]);
binary_prelude(to);
let AssemblySpec { code_length, data_length, entry } =
self.assemble_reachable(entry, types, to);
self.assemble_reachable(entry, types, files, to);
let exe = AbleOsExecutableHeader {
magic_number: [0x15, 0x91, 0xD2],
@ -138,6 +146,7 @@ impl Backend for HbvmBackend {
&mut self,
from: ty::Func,
types: &Types,
_files: &EntSlice<Module, parser::Ast>,
to: &mut Vec<u8>,
) -> AssemblySpec {
debug_assert!(self.asm.frontier.is_empty());
@ -215,7 +224,7 @@ impl Backend for HbvmBackend {
types: &'a Types,
files: &'a EntSlice<Module, parser::Ast>,
output: &mut String,
) -> Result<(), hbbytecode::DisasmError<'a>> {
) -> Result<(), alloc::boxed::Box<dyn error::Error + Send + Sync + 'a>> {
use hbbytecode::DisasmItem;
let functions = types
.ins
@ -250,7 +259,7 @@ impl Backend for HbvmBackend {
}),
)
.collect::<BTreeMap<_, _>>();
hbbytecode::disasm(&mut sluce, &functions, output, eca_handler)
hbbytecode::disasm(&mut sluce, &functions, output, eca_handler).map_err(Into::into)
}
fn emit_ct_body(

View file

@ -1,6 +1,6 @@
use {
crate::{
backend::hbvm::HbvmBackend,
backend::{hbvm::HbvmBackend, Backend},
parser::{Ast, Ctx, FileKind},
son::{self},
ty, FnvBuildHasher,
@ -12,7 +12,6 @@ use {
borrow::ToOwned,
collections::VecDeque,
eprintln,
ffi::OsStr,
io::{self, Write as _},
path::{Path, PathBuf},
string::ToString,
@ -72,6 +71,7 @@ pub struct Options<'a> {
pub dump_asm: bool,
pub extra_threads: usize,
pub resolver: Option<PathResolver<'a>>,
pub backend: Option<&'a mut dyn Backend>,
}
impl<'a> Options<'a> {
@ -79,6 +79,7 @@ impl<'a> Options<'a> {
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>")?;
@ -124,6 +125,7 @@ impl<'a> Options<'a> {
)
})
.transpose()?,
backend,
})
}
}
@ -158,10 +160,11 @@ pub fn run_compiler(
write!(out, "{}", &parsed.ast[0])?;
} else {
let mut backend = HbvmBackend::default();
let backend = options.backend.unwrap_or(&mut backend);
let mut ctx = crate::son::CodegenCtx::default();
*ctx.parser.errors.get_mut() = parsed.errors;
let mut codegen = son::Codegen::new(&mut backend, &parsed.ast, &mut ctx);
let mut codegen = son::Codegen::new(backend, &parsed.ast, &mut ctx);
codegen.push_embeds(parsed.embeds);
codegen.generate(ty::Module::MAIN);
@ -299,7 +302,7 @@ pub struct CantLoadFile {
impl core::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "can't load file: {}", display_rel_path(&self.path),)
write!(f, "can't load file: {}", crate::display_rel_path(&self.path),)
}
}
@ -350,7 +353,7 @@ pub fn parse_from_fs(
if !physiscal_path.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("can't find file: {}", display_rel_path(&physiscal_path)),
format!("can't find file: {}", crate::display_rel_path(&physiscal_path)),
));
}
@ -377,7 +380,7 @@ pub fn parse_from_fs(
e.kind(),
format!(
"can't load embed file: {}: {e}",
display_rel_path(&physiscal_path)
crate::display_rel_path(&physiscal_path)
),
)
})?;
@ -395,7 +398,7 @@ pub fn parse_from_fs(
let path = path.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", display_rel_path(&path)),
format!("path contains invalid characters: {}", crate::display_rel_path(&path)),
)
})?;
Ok(Ast::new(path, std::fs::read_to_string(path)?, ctx, &mut |path, from, kind| {
@ -441,9 +444,3 @@ pub fn parse_from_fs(
errors,
})
}
pub fn display_rel_path(path: &(impl AsRef<OsStr> + ?Sized)) -> std::path::Display {
static CWD: std::sync::LazyLock<PathBuf> =
std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default());
std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display()
}

View file

@ -33,8 +33,10 @@
#[cfg(feature = "std")]
pub use fs::*;
pub use utils::Ent;
use {self::ty::Builtin, alloc::vec::Vec};
use {
self::{ty::Builtin, utils::Ent},
alloc::vec::Vec,
};
#[macro_use]
extern crate alloc;
@ -72,6 +74,7 @@ pub mod backend {
utils::EntSlice,
},
alloc::{string::String, vec::Vec},
core::error,
};
pub mod hbvm;
@ -87,6 +90,7 @@ pub mod backend {
&mut self,
from: ty::Func,
types: &Types,
files: &EntSlice<Module, parser::Ast>,
to: &mut Vec<u8>,
) -> AssemblySpec;
fn disasm<'a>(
@ -96,11 +100,11 @@ pub mod backend {
types: &'a Types,
files: &'a EntSlice<Module, parser::Ast>,
output: &mut String,
) -> Result<(), hbbytecode::DisasmError<'a>>;
) -> Result<(), alloc::boxed::Box<dyn error::Error + Send + Sync + 'a>>;
fn emit_body(
&mut self,
id: ty::Func,
ci: &Nodes,
nodes: &Nodes,
tys: &Types,
files: &EntSlice<Module, parser::Ast>,
);
@ -108,20 +112,26 @@ pub mod backend {
fn emit_ct_body(
&mut self,
id: ty::Func,
ci: &Nodes,
nodes: &Nodes,
tys: &Types,
files: &EntSlice<Module, parser::Ast>,
) {
self.emit_body(id, ci, tys, files);
self.emit_body(id, nodes, tys, files);
}
fn assemble_bin(&mut self, from: ty::Func, types: &Types, to: &mut Vec<u8>) {
self.assemble_reachable(from, types, to);
fn assemble_bin(
&mut self,
from: ty::Func,
types: &Types,
files: &EntSlice<Module, parser::Ast>,
to: &mut Vec<u8>,
) {
self.assemble_reachable(from, types, files, to);
}
}
}
mod utils;
pub mod utils;
mod debug {
use core::fmt::Debug;
@ -521,3 +531,15 @@ fn test_parse_files(
embed_map.iter().map(|&(_, content)| content.to_owned().into_bytes()).collect(),
)
}
#[cfg(feature = "std")]
pub fn display_rel_path(path: &(impl AsRef<std::ffi::OsStr> + ?Sized)) -> std::path::Display {
static CWD: std::sync::LazyLock<std::path::PathBuf> =
std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default());
std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display()
}
#[cfg(not(feature = "std"))]
pub fn display_rel_path(path: &str) -> &str {
path
}

View file

@ -1461,10 +1461,7 @@ impl<D: core::fmt::Display> core::fmt::Display for Report<'_, D> {
fn report_to(file: &str, path: &str, pos: Pos, msg: &dyn fmt::Display, out: &mut impl fmt::Write) {
let (line, mut col) = lexer::line_col(file.as_bytes(), pos);
#[cfg(feature = "std")]
let disp = crate::fs::display_rel_path(path);
#[cfg(not(feature = "std"))]
let disp = path;
let disp = crate::display_rel_path(path);
_ = writeln!(out, "{}:{}:{}: {}", disp, line, col, msg);
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)

View file

@ -27,10 +27,10 @@ use {
core::{
assert_matches::debug_assert_matches,
cell::RefCell,
error,
fmt::{self, Debug, Display, Write},
format_args as fa, mem,
},
hbbytecode::DisasmError,
};
const DEFAULT_ACLASS: usize = 0;
@ -616,16 +616,20 @@ impl<'a> Codegen<'a> {
pub fn assemble_comptime(&mut self) -> Comptime {
self.ct.code.clear();
self.backend.assemble_bin(ty::Func::MAIN, self.tys, &mut self.ct.code);
self.backend.assemble_bin(ty::Func::MAIN, self.tys, self.files, &mut self.ct.code);
self.ct.reset();
core::mem::take(self.ct)
}
pub fn assemble(&mut self, buf: &mut Vec<u8>) {
self.backend.assemble_bin(ty::Func::MAIN, self.tys, buf);
self.backend.assemble_bin(ty::Func::MAIN, self.tys, self.files, buf);
}
pub fn disasm(&mut self, output: &mut String, bin: &[u8]) -> Result<(), DisasmError> {
pub fn disasm(
&mut self,
output: &mut String,
bin: &[u8],
) -> Result<(), alloc::boxed::Box<dyn error::Error + Send + Sync + '_>> {
self.backend.disasm(bin, &mut |_| {}, self.tys, self.files, output)
}
@ -669,7 +673,8 @@ impl<'a> Codegen<'a> {
// TODO: return them back
let entry = self.ct_backend.assemble_reachable(fuc, self.tys, &mut self.ct.code).entry;
let entry =
self.ct_backend.assemble_reachable(fuc, self.tys, self.files, &mut self.ct.code).entry;
#[cfg(debug_assertions)]
{
@ -4403,8 +4408,24 @@ mod tests {
core::fmt::Write,
};
pub struct Logger;
impl log::Log for Logger {
fn enabled(&self, _: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
std::eprintln!("{}", record.args())
}
}
fn flush(&self) {}
}
fn generate(ident: &str, input: &str, output: &mut String) {
_ = log::set_logger(&crate::fs::Logger);
_ = log::set_logger(&Logger);
log::set_max_level(log::LevelFilter::Info);
//log::set_max_level(log::LevelFilter::Trace);

View file

@ -83,7 +83,7 @@ pub enum Arg {
}
impl ArgIter {
pub(crate) fn next(&mut self, tys: &Types) -> Option<Arg> {
pub fn next(&mut self, tys: &Types) -> Option<Arg> {
let ty = tys.ins.args[self.0.next()?];
if ty == Id::TYPE {
return Some(Arg::Type(tys.ins.args[self.0.next().unwrap()]));
@ -91,7 +91,7 @@ impl ArgIter {
Some(Arg::Value(ty))
}
pub(crate) fn next_value(&mut self, tys: &Types) -> Option<Id> {
pub fn next_value(&mut self, tys: &Types) -> Option<Id> {
loop {
match self.next(tys)? {
Arg::Type(_) => continue,
@ -299,6 +299,10 @@ impl Id {
_ => false,
}
}
pub fn is_aggregate(&self, tys: &Types) -> bool {
self.loc(tys) == Loc::Stack
}
}
#[derive(PartialEq, Eq, Clone, Copy)]

View file

@ -363,6 +363,10 @@ impl Vc {
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn len_mut(&mut self) -> &mut Nid {
unsafe {
if self.is_inline() {

3
smh.hb
View file

@ -0,0 +1,3 @@
main := fn(): uint {
return 69
}