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