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:
parent
08fc9d6ab6
commit
ee67ebb017
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,6 +2,9 @@
|
|||
/target
|
||||
rustc-ice-*
|
||||
|
||||
a.out
|
||||
out.o
|
||||
|
||||
# sqlite
|
||||
db.sqlite
|
||||
db.sqlite-journal
|
||||
|
|
231
Cargo.lock
generated
231
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
10
c/Cargo.toml
Normal 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"] }
|
|
@ -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)
|
12
cranelift-backend/Cargo.toml
Normal file
12
cranelift-backend/Cargo.toml
Normal 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"
|
534
cranelift-backend/src/lib.rs
Normal file
534
cranelift-backend/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
299
cranelift-backend/src/x86_64.rs
Normal file
299
cranelift-backend/src/x86_64.rs
Normal 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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ use {
|
|||
backend::hbvm::HbvmBackend,
|
||||
son::{Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
Ent,
|
||||
utils::Ent,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue