Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
69e7ad0642 | |||
7f3984ad9a | |||
bfc92431ea | |||
ede18f86f8 | |||
31c501c643 | |||
0d118c17b2 | |||
0ae0cae825 | |||
da69b705f1 |
|
@ -1,4 +1,2 @@
|
|||
[alias]
|
||||
xtask = "r -p xtask --"
|
||||
wasm-build = "b --target wasm32-unknown-unknown --profile=small -Zbuild-std=core,alloc -Zbuild-std-features=optimize_for_size,panic_immediate_abort -p"
|
||||
wasm-build-debug = "b --target wasm32-unknown-unknown --profile=small-dev -Zbuild-std=core,alloc -Zbuild-std-features=optimize_for_size -p"
|
||||
|
|
19
.gitignore
vendored
19
.gitignore
vendored
|
@ -1,19 +1,4 @@
|
|||
# garbage
|
||||
/target
|
||||
/hbbytecode/src/instrs.rs
|
||||
/.rgignore
|
||||
rustc-ice-*
|
||||
|
||||
a.out
|
||||
out.o
|
||||
/examples/raylib/main
|
||||
|
||||
# sqlite
|
||||
db.sqlite
|
||||
db.sqlite-journal
|
||||
|
||||
# assets
|
||||
/depell/src/*.gz
|
||||
/depell/src/*.wasm
|
||||
/depell/src/static-pages/*.html
|
||||
#**/*-sv.rs
|
||||
/bytecode/src/instrs.rs
|
||||
/lang/src/testcases.rs
|
||||
|
|
1968
Cargo.lock
generated
1968
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
39
Cargo.toml
39
Cargo.toml
|
@ -1,50 +1,17 @@
|
|||
cargo-features = ["profile-rustflags"]
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"bytecode",
|
||||
"vm",
|
||||
"xrt",
|
||||
"xtask",
|
||||
"lang",
|
||||
"depell",
|
||||
"depell/wasm-fmt",
|
||||
"depell/wasm-hbc",
|
||||
"depell/wasm-rt",
|
||||
"cranelift-backend",
|
||||
"c",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
hbbytecode = { path = "bytecode", default-features = false }
|
||||
hbvm = { path = "vm", default-features = false }
|
||||
hblang = { path = "lang", default-features = false }
|
||||
members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
#debug = true
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[profile.small]
|
||||
rustflags = ["-Zfmt-debug=none", "-Zlocation-detail=none"]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
strip = "debuginfo"
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[profile.small-dev]
|
||||
inherits = "dev"
|
||||
opt-level = "z"
|
||||
strip = "debuginfo"
|
||||
panic = "abort"
|
||||
|
||||
[profile.fuzz]
|
||||
inherits = "dev"
|
||||
debug = true
|
||||
opt-level = 3
|
||||
panic = "abort"
|
||||
|
|
11
c/Cargo.toml
11
c/Cargo.toml
|
@ -1,11 +0,0 @@
|
|||
[package]
|
||||
name = "hbc"
|
||||
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"
|
||||
target-lexicon = { version = "0.12", features = ["std"] }
|
|
@ -1,95 +0,0 @@
|
|||
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 {
|
||||
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 = if target
|
||||
== target_lexicon::Triple::from_str(hblang::backend::hbvm::TARGET_TRIPLE).unwrap()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
native.insert(
|
||||
cranelift_backend::Backend::new(target, &backend_flags)
|
||||
.map_err(io::Error::other)?,
|
||||
) as &mut dyn hblang::backend::Backend,
|
||||
)
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
log::set_logger(&hblang::fs::Logger).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
let mut out = Vec::new();
|
||||
let mut warnings = String::new();
|
||||
match run(&mut out, &mut warnings) {
|
||||
Ok(_) => {
|
||||
std::io::stderr().write_all(warnings.as_bytes()).unwrap();
|
||||
std::io::stdout().write_all(&out).unwrap()
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
[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"
|
|
@ -1,994 +0,0 @@
|
|||
#![feature(if_let_guard)]
|
||||
#![feature(slice_take)]
|
||||
use {
|
||||
core::panic,
|
||||
cranelift_codegen::{
|
||||
self as cc, CodegenError, Final, FinalizedMachReloc, MachBufferFinalized,
|
||||
ir::{self as cir, InstBuilder, MemFlags, TrapCode, UserExternalName, condcodes},
|
||||
isa::{LookupError, TargetIsa},
|
||||
settings::{Configurable, SetError},
|
||||
},
|
||||
cranelift_frontend::{self as cf, FunctionBuilder},
|
||||
cranelift_module::{self as cm, Module, ModuleError},
|
||||
hblang::{
|
||||
lexer::TokenKind,
|
||||
nodes::{self as hbnodes},
|
||||
ty as hbty,
|
||||
utils::{self as hbutils, Ent, EntVec},
|
||||
},
|
||||
std::{
|
||||
collections::HashSet,
|
||||
fmt::{Display, Write},
|
||||
ops::Range,
|
||||
},
|
||||
};
|
||||
|
||||
mod x86_64;
|
||||
|
||||
pub struct Backend {
|
||||
ctx: cc::Context,
|
||||
dt_ctx: cm::DataDescription,
|
||||
fb_ctx: cf::FunctionBuilderContext,
|
||||
module: Option<cranelift_object::ObjectModule>,
|
||||
ctrl_plane: cc::control::ControlPlane,
|
||||
funcs: Functions,
|
||||
globals: EntVec<hbty::Global, Global>,
|
||||
asm: Assembler,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
pub fn new(triple: target_lexicon::Triple, flags: &str) -> Result<Self, BackendCreationError> {
|
||||
Ok(Self {
|
||||
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(
|
||||
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",
|
||||
cm::default_libcall_names(),
|
||||
)?)
|
||||
.into(),
|
||||
funcs: Default::default(),
|
||||
globals: Default::default(),
|
||||
asm: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hblang::backend::Backend for Backend {
|
||||
fn triple(&self) -> String {
|
||||
self.module.as_ref().unwrap().isa().triple().to_string()
|
||||
}
|
||||
|
||||
fn assemble_reachable(
|
||||
&mut self,
|
||||
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());
|
||||
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) -> hbty::Id {
|
||||
match name.namespace {
|
||||
0 => hbty::Kind::Func(hbty::Func::new(name.index as _)),
|
||||
1 => hbty::Kind::Global(hbty::Global::new(name.index as _)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.compress()
|
||||
}
|
||||
|
||||
self.globals.shadow(types.ins.globals.len());
|
||||
|
||||
let mut seen_names = HashSet::new();
|
||||
|
||||
self.asm.frontier.push(from.into());
|
||||
while let Some(itm) = self.asm.frontier.pop() {
|
||||
match itm.expand() {
|
||||
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.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 if fd.is_import {
|
||||
self.asm.name.push_str(file.ident_str(fd.name));
|
||||
} else {
|
||||
self.asm.name.push_str(hblang::strip_cwd(&file.path));
|
||||
self.asm.name.push('.');
|
||||
if fd.parent != hbty::Id::from(fd.file) {
|
||||
write!(
|
||||
self.asm.name,
|
||||
"{}",
|
||||
hbty::Display::new(types, files, fd.parent)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
self.asm.name.push_str(file.ident_str(fd.name));
|
||||
if fd.is_generic {
|
||||
let mut args = fd.sig.args.args();
|
||||
self.asm.name.push('(');
|
||||
while let Some(arg) = args.next(types) {
|
||||
if let hbty::Arg::Type(ty) = arg {
|
||||
write!(
|
||||
self.asm.name,
|
||||
"{},",
|
||||
hbty::Display::new(types, files, ty)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
self.asm.name.pop().unwrap();
|
||||
self.asm.name.push(')');
|
||||
}
|
||||
}
|
||||
let linkage = if func == from {
|
||||
cm::Linkage::Export
|
||||
} else if fd.is_import {
|
||||
cm::Linkage::Import
|
||||
} else {
|
||||
cm::Linkage::Local
|
||||
};
|
||||
build_signature(
|
||||
module.isa().default_call_conv(),
|
||||
fd.sig,
|
||||
types,
|
||||
&mut self.ctx.func.signature,
|
||||
&mut vec![],
|
||||
);
|
||||
debug_assert!(seen_names.insert(self.asm.name.clone()), "{}", self.asm.name);
|
||||
fuc.module_id = Some(
|
||||
module
|
||||
.declare_function(&self.asm.name, linkage, &self.ctx.func.signature)
|
||||
.unwrap(),
|
||||
);
|
||||
if !fd.is_import {
|
||||
self.asm.funcs.push(func);
|
||||
}
|
||||
}
|
||||
hbty::Kind::Global(glob) => {
|
||||
if self.globals[glob].module_id.is_some() {
|
||||
continue;
|
||||
}
|
||||
self.asm.globals.push(glob);
|
||||
|
||||
self.asm.name.clear();
|
||||
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, cm::Linkage::Local, mutable, false)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
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();
|
||||
}
|
||||
let prev_len = self.ctx.func.params.user_named_funcs().len();
|
||||
self.ctx.func.params.ensure_user_func_name(nm.clone());
|
||||
debug_assert_ne!(self.ctx.func.params.user_named_funcs().len(), prev_len, "{}", nm);
|
||||
});
|
||||
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 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!()
|
||||
}
|
||||
|
||||
fn emit_body(
|
||||
&mut self,
|
||||
id: hbty::Func,
|
||||
nodes: &hbnodes::Nodes,
|
||||
tys: &hbty::Types,
|
||||
files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>,
|
||||
) {
|
||||
let isa = self.module.as_ref().unwrap().isa();
|
||||
|
||||
let mut lens = vec![];
|
||||
let stack_ret = build_signature(
|
||||
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),
|
||||
isa,
|
||||
nodes,
|
||||
tys,
|
||||
files,
|
||||
values: &mut vec![None; nodes.len()],
|
||||
arg_lens: &lens,
|
||||
stack_ret,
|
||||
}
|
||||
.build(tys.ins.funcs[id].sig);
|
||||
|
||||
self.ctx.func.name =
|
||||
cir::UserFuncName::User(cir::UserExternalName { namespace: 0, index: id.index() as _ });
|
||||
|
||||
//std::eprintln!("{}", self.ctx.func.display());
|
||||
|
||||
self.ctx.compile(isa, &mut self.ctrl_plane).unwrap();
|
||||
let code = self.ctx.compiled_code().unwrap();
|
||||
self.funcs.push(id, &self.ctx.func, &code.buffer);
|
||||
self.ctx.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn build_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 {
|
||||
cc::isa::CallConv::SystemV => {
|
||||
x86_64::build_systemv_signature(sig, types, signature, arg_meta)
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct AbiMeta {
|
||||
trough_mem: bool,
|
||||
arg_count: usize,
|
||||
}
|
||||
|
||||
struct FuncBuilder<'a, 'b> {
|
||||
bl: cf::FunctionBuilder<'b>,
|
||||
isa: &'a dyn TargetIsa,
|
||||
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: hbty::Sig) {
|
||||
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).to_vec()[..];
|
||||
|
||||
if self.stack_ret {
|
||||
let ret_ptr = *arg_vals.take_first().unwrap();
|
||||
self.values[hbnodes::MEM as usize] = Some(Ok(ret_ptr));
|
||||
}
|
||||
|
||||
let Self { nodes, tys, .. } = self;
|
||||
|
||||
let mut parama_len = self.arg_lens[1..].iter();
|
||||
let mut typs = sig.args.args();
|
||||
let mut args = nodes[hbnodes::VOID].outputs[hbnodes::ARG_START..].iter();
|
||||
while let Some(aty) = typs.next(tys) {
|
||||
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(cir::StackSlotData {
|
||||
kind: cir::StackSlotKind::ExplicitSlot,
|
||||
size: self.tys.size_of(ty),
|
||||
align_shift: self.tys.align_of(ty).ilog2() as _,
|
||||
});
|
||||
let loc = arg_vals.take(..abi_meta.arg_count).unwrap();
|
||||
assert!(loc.len() <= 2, "NEED handling");
|
||||
let align =
|
||||
loc.iter().map(|&p| self.bl.func.dfg.value_type(p).bytes()).max().unwrap();
|
||||
let mut offset = 0i32;
|
||||
for &v in loc {
|
||||
self.bl.ins().stack_store(v, slot, offset);
|
||||
offset += align as i32;
|
||||
}
|
||||
self.values[arg as usize] =
|
||||
Some(Ok(self.bl.ins().stack_addr(cir::types::I64, slot, 0)))
|
||||
} else {
|
||||
let loc = arg_vals.take(..abi_meta.arg_count).unwrap();
|
||||
debug_assert_eq!(loc.len(), 1);
|
||||
self.values[arg as usize] = Some(Ok(loc[0]));
|
||||
}
|
||||
}
|
||||
|
||||
self.values[hbnodes::ENTRY as usize] = Some(Err(entry));
|
||||
|
||||
self.emit_node(hbnodes::VOID, hbnodes::VOID);
|
||||
|
||||
self.bl.finalize();
|
||||
}
|
||||
|
||||
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: hbnodes::Nid) -> cir::Block {
|
||||
self.values[nid as usize].unwrap().unwrap_err()
|
||||
}
|
||||
|
||||
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: hbnodes::Nid, block: hbnodes::Nid) {
|
||||
use hbnodes::*;
|
||||
|
||||
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[o].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());
|
||||
self.close_block(block);
|
||||
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, self.nodes[o].ty.to_clif(self.tys))));
|
||||
}
|
||||
}
|
||||
self.bl.ins().jump(next, &args);
|
||||
self.close_block(block);
|
||||
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::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, self.nodes[o].ty.to_clif(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::Region => {
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Kind::Die => {
|
||||
self.bl.ins().trap(TrapCode::unwrap_user(1));
|
||||
self.close_block(block);
|
||||
self.emit_node(node.outputs[0], block);
|
||||
Err(self.block_of(block))
|
||||
}
|
||||
Kind::Return { .. } => {
|
||||
let mut ir_args = vec![];
|
||||
if node.inputs[1] == hbnodes::VOID {
|
||||
} else {
|
||||
let abi_meta = self.arg_lens[0];
|
||||
let arg = node.inputs[1];
|
||||
if !abi_meta.trough_mem && self.nodes[node.inputs[1]].ty.is_aggregate(self.tys)
|
||||
{
|
||||
let loc = self.bl.func.signature.returns.clone();
|
||||
assert!(loc.len() <= 2, "NEED handling");
|
||||
let align = loc.iter().map(|&p| p.value_type.bytes()).max().unwrap();
|
||||
let mut offset = 0i32;
|
||||
let src = self.value_of(self.nodes[arg].inputs[1]);
|
||||
debug_assert!(self.nodes[arg].kind == Kind::Load);
|
||||
for &v in &loc {
|
||||
ir_args.push(self.bl.ins().load(
|
||||
v.value_type,
|
||||
MemFlags::new(),
|
||||
src,
|
||||
offset,
|
||||
));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
self.bl.ins().return_(&ir_args);
|
||||
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, args } => {
|
||||
assert_ne!(func, hbty::Func::ECA, "@eca is not supported");
|
||||
|
||||
if unreachable {
|
||||
todo!()
|
||||
} else {
|
||||
let mut arg_lens = vec![];
|
||||
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,
|
||||
self.tys,
|
||||
&mut signature,
|
||||
&mut arg_lens,
|
||||
);
|
||||
|
||||
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
|
||||
== cir::ExternalName::user(user_name_ref)
|
||||
}) {
|
||||
break 'b id;
|
||||
}
|
||||
|
||||
let signature = self.bl.func.import_signature(signature.clone());
|
||||
|
||||
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![];
|
||||
|
||||
if stack_ret {
|
||||
ir_args.push(self.value_of(*node.inputs.last().unwrap()));
|
||||
}
|
||||
|
||||
let mut params = signature.params.as_slice();
|
||||
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 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();
|
||||
assert!(loc.len() <= 2, "NEED handling");
|
||||
let align = loc.iter().map(|&p| p.value_type.bytes()).max().unwrap();
|
||||
let mut offset = 0i32;
|
||||
let src = self.value_of(self.nodes[arg].inputs[1]);
|
||||
debug_assert!(self.nodes[arg].kind == Kind::Load);
|
||||
for &v in loc {
|
||||
ir_args.push(self.bl.ins().load(
|
||||
v.value_type,
|
||||
MemFlags::new(),
|
||||
src,
|
||||
offset,
|
||||
));
|
||||
offset += align as i32;
|
||||
}
|
||||
} else {
|
||||
let loc = params.take(..abi_meta.arg_count).unwrap();
|
||||
debug_assert_eq!(loc.len(), 1);
|
||||
ir_args.push(self.value_of(arg));
|
||||
}
|
||||
}
|
||||
|
||||
let inst = self.bl.ins().call(func_ref, &ir_args);
|
||||
match *self.bl.inst_results(inst) {
|
||||
[] => {}
|
||||
[scala] => self.values[nid as usize] = Some(Ok(scala)),
|
||||
[a, b] => {
|
||||
assert!(!stack_ret);
|
||||
let slot = self.value_of(*node.inputs.last().unwrap());
|
||||
|
||||
let loc = [a, b];
|
||||
assert!(loc.len() <= 2, "NEED handling");
|
||||
let align = loc
|
||||
.iter()
|
||||
.map(|&p| self.bl.func.dfg.value_type(p).bytes())
|
||||
.max()
|
||||
.unwrap();
|
||||
let mut offset = 0i32;
|
||||
for v in loc {
|
||||
self.bl.ins().store(MemFlags::new(), v, slot, offset);
|
||||
offset += align as i32;
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
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::BinOp { op } => {
|
||||
let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
|
||||
let [lh, rh] = [self.value_of(lhs), self.value_of(rhs)];
|
||||
|
||||
let is_int_op = node.ty.is_integer()
|
||||
|| node.ty.is_pointer()
|
||||
|| (node.ty == hbty::Id::BOOL
|
||||
&& (self.nodes[lhs].ty.is_integer()
|
||||
|| node.ty.is_pointer()
|
||||
|| 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());
|
||||
|
||||
Ok(if is_int_op {
|
||||
let signed = node.ty.is_signed();
|
||||
match op {
|
||||
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(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(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(op.to_int_cc(signed), lh, rh),
|
||||
op => todo!("{op}"),
|
||||
}
|
||||
} else if is_float_op {
|
||||
match op {
|
||||
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(op.to_float_cc(), lh, rh),
|
||||
op => todo!("{op}"),
|
||||
}
|
||||
} else {
|
||||
todo!("{}", hbty::Display::new(self.tys, self.files, node.ty))
|
||||
})
|
||||
}
|
||||
Kind::RetVal => Ok(self.value_of(node.inputs[0])),
|
||||
Kind::UnOp { op } => {
|
||||
let oper = self.value_of(node.inputs[1]);
|
||||
let dst = node.ty;
|
||||
let src = self
|
||||
.tys
|
||||
.inner_of(self.nodes[node.inputs[1]].ty)
|
||||
.unwrap_or(self.nodes[node.inputs[1]].ty);
|
||||
|
||||
let dty = dst.to_clif(self.tys);
|
||||
Ok(match op {
|
||||
TokenKind::Sub => self.bl.ins().ineg(oper),
|
||||
TokenKind::Not => self.bl.ins().bnot(oper),
|
||||
TokenKind::Float if dst.is_float() && src.is_unsigned() => {
|
||||
self.bl.ins().fcvt_from_uint(dty, oper)
|
||||
}
|
||||
TokenKind::Float if dst.is_float() && src.is_signed() => {
|
||||
self.bl.ins().fcvt_from_sint(dty, oper)
|
||||
}
|
||||
TokenKind::Number if src.is_float() && dst.is_unsigned() => {
|
||||
self.bl.ins().fcvt_to_uint(dty, oper)
|
||||
}
|
||||
TokenKind::Number
|
||||
if src.is_signed() && (dst.is_integer() || dst.is_pointer()) =>
|
||||
{
|
||||
self.bl.ins().sextend(dty, oper)
|
||||
}
|
||||
TokenKind::Number
|
||||
if (src.is_unsigned() || src == hbty::Id::BOOL)
|
||||
&& (dst.is_integer() || dst.is_pointer()) =>
|
||||
{
|
||||
self.bl.ins().uextend(dty, oper)
|
||||
}
|
||||
TokenKind::Float if dst == hbty::Id::F64 && src.is_float() => {
|
||||
self.bl.ins().fpromote(dty, oper)
|
||||
}
|
||||
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(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(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(cir::UserExternalName {
|
||||
namespace: 1,
|
||||
index: global.index() as u32,
|
||||
});
|
||||
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(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(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]);
|
||||
let dest = self.value_of(node.inputs[2]);
|
||||
self.bl.emit_small_memory_copy(
|
||||
self.isa.frontend_config(),
|
||||
dest,
|
||||
src,
|
||||
self.tys.size_of(node.ty) as _,
|
||||
self.tys.align_of(node.ty) as _,
|
||||
self.tys.align_of(node.ty) as _,
|
||||
false,
|
||||
MemFlags::new(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Kind::Stre => {
|
||||
let value = self.value_of(node.inputs[1]);
|
||||
let ptr = self.value_of(node.inputs[2]);
|
||||
self.bl.ins().store(MemFlags::new(), value, ptr, 0);
|
||||
return;
|
||||
}
|
||||
Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops | Kind::Join => return,
|
||||
Kind::Assert { .. } => unreachable!(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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<cm::DataId>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FuncHeaders {
|
||||
module_id: Option<cm::FuncId>,
|
||||
alignment: u32,
|
||||
code: Range<u32>,
|
||||
relocs: Range<u32>,
|
||||
external_names: Range<u32>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Functions {
|
||||
headers: EntVec<hbty::Func, FuncHeaders>,
|
||||
code: Vec<u8>,
|
||||
relocs: Vec<FinalizedMachReloc>,
|
||||
external_names: Vec<UserExternalName>,
|
||||
}
|
||||
|
||||
impl Functions {
|
||||
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,
|
||||
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<hbty::Id>,
|
||||
globals: Vec<hbty::Global>,
|
||||
funcs: Vec<hbty::Func>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BackendCreationError {
|
||||
UnsupportedTriplet(LookupError),
|
||||
InvalidFlags(CodegenError),
|
||||
UnsupportedModuleConfig(ModuleError),
|
||||
InvalidFlag { key: String, value: String, err: SetError },
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
BackendCreationError::InvalidFlag { key, value, err } => {
|
||||
write!(
|
||||
f,
|
||||
"Problem setting a '{key}' to '{value}': {err}\navailable flags: {}",
|
||||
cc::settings::Flags::new(cc::settings::builder())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -1,310 +0,0 @@
|
|||
// 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, hblang::ty};
|
||||
|
||||
pub fn build_systemv_signature(
|
||||
sig: hblang::ty::Sig,
|
||||
types: &hblang::ty::Types,
|
||||
signature: &mut cranelift_codegen::ir::Signature,
|
||||
arg_lens: &mut Vec<AbiMeta>,
|
||||
) -> bool {
|
||||
let mut alloca = Alloca::new();
|
||||
|
||||
alloca.next(false, sig.ret, types, &mut signature.returns);
|
||||
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();
|
||||
while let Some(arg) = args.next_value(types) {
|
||||
let prev = signature.params.len();
|
||||
let trough_mem = alloca.next(true, arg, types, &mut signature.params);
|
||||
arg_lens.push(AbiMeta { arg_count: signature.params.len() - prev, trough_mem });
|
||||
}
|
||||
|
||||
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() || layout == ty::Id::BOOL => 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(());
|
||||
}
|
||||
|
||||
ty => unimplemented!("{ty:?}"),
|
||||
};
|
||||
|
||||
// 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>,
|
||||
) -> bool {
|
||||
if cx.size_of(arg) == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
true
|
||||
}
|
||||
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(),
|
||||
));
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "depell"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
argon2 = "0.5.3"
|
||||
axum = "0.7.7"
|
||||
axum-server = { version = "0.7.1", optional = true, features = ["rustls", "tls-rustls"] }
|
||||
const_format = "0.2.33"
|
||||
getrandom = "0.2.15"
|
||||
hblang.workspace = true
|
||||
htmlm = "0.5.0"
|
||||
log = "0.4.22"
|
||||
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
||||
rusqlite = { version = "0.32.1", features = ["bundled"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
time = "0.3.36"
|
||||
tokio = { version = "1.40.0", features = ["rt"] }
|
||||
|
||||
[features]
|
||||
#default = ["tls"]
|
||||
tls = ["dep:axum-server"]
|
|
@ -1,14 +0,0 @@
|
|||
# Depell
|
||||
|
||||
Depell is a website that allows users to import/post/run hblang code and create huge dependency graphs. Its currently hosted at https://depell.mlokis.tech.
|
||||
|
||||
## Local Development
|
||||
|
||||
Prerequirements:
|
||||
- rust nigthly toolchain: install rust from [here](https://www.rust-lang.org/tools/install)
|
||||
|
||||
```bash
|
||||
rustup default nightly
|
||||
cargo xtask watch-depell-debug
|
||||
# browser http://localhost:8080
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
|
Before Width: | Height: | Size: 279 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M320-200v-560l440 280-440 280Zm80-280Zm0 134 210-134-210-134v268Z"/></svg>
|
Before Width: | Height: | Size: 190 B |
|
@ -1,213 +0,0 @@
|
|||
* {
|
||||
font-family: var(--font);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
body {
|
||||
--primary: light-dark(white, #181A1B);
|
||||
--secondary: light-dark(#EFEFEF, #212425);
|
||||
--timestamp: light-dark(#555555, #AAAAAA);
|
||||
--error: #ff3333;
|
||||
}
|
||||
|
||||
body {
|
||||
--small-gap: 5px;
|
||||
--font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
--monospace: 'Courier New', Courier, monospace;
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
section:last-child {
|
||||
display: flex;
|
||||
gap: var(--small-gap);
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: var(--small-gap);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--small-gap);
|
||||
}
|
||||
}
|
||||
|
||||
div.preview {
|
||||
margin: var(--small-gap) 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--small-gap);
|
||||
|
||||
div.info {
|
||||
display: flex;
|
||||
gap: var(--small-gap);
|
||||
|
||||
span[apply=timestamp] {
|
||||
color: var(--timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
div.stat {
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
div.code {
|
||||
position: relative;
|
||||
|
||||
nav {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: var(--small-gap);
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--small-gap);
|
||||
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
textarea {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: var(--secondary);
|
||||
padding: var(--small-gap);
|
||||
padding-top: calc(var(--small-gap) * 1.5);
|
||||
font-family: var(--monospace);
|
||||
resize: none;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--secondary);
|
||||
padding: var(--small-gap);
|
||||
padding-top: calc(var(--small-gap) * 1.5);
|
||||
margin: 0px;
|
||||
font-family: var(--monospace);
|
||||
tab-size: 4;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: inherit;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: var(--secondary);
|
||||
padding: var(--small-gap);
|
||||
}
|
||||
|
||||
input:is(:hover, :focus) {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: inherit;
|
||||
background: var(--secondary);
|
||||
}
|
||||
|
||||
button:hover:not(:active) {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--monospace);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div#code-editor {
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
textarea {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
span#code-size {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
div#dep-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--small-gap);
|
||||
|
||||
section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: var(--small-gap);
|
||||
|
||||
div {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.syn {
|
||||
font-family: var(--monospace);
|
||||
|
||||
&.Comment {
|
||||
color: #939f91;
|
||||
}
|
||||
|
||||
&.Keyword {
|
||||
color: #f85552;
|
||||
}
|
||||
|
||||
&.Identifier,
|
||||
&.Directive {
|
||||
color: #3a94c5;
|
||||
}
|
||||
|
||||
/* &.Number {} */
|
||||
|
||||
&.String {
|
||||
color: #8da101;
|
||||
}
|
||||
|
||||
&.Op,
|
||||
&.Assign {
|
||||
color: #f57d26;
|
||||
}
|
||||
|
||||
&.Paren,
|
||||
&.Bracket,
|
||||
&.Comma,
|
||||
&.Dot,
|
||||
&.Ctor,
|
||||
&.Colon {
|
||||
color: light-dark(#5c6a72, #999999);
|
||||
}
|
||||
}
|
|
@ -1,554 +0,0 @@
|
|||
/// @ts-check
|
||||
|
||||
/** @return {never} */
|
||||
function never() { throw new Error() }
|
||||
|
||||
/**@type{WebAssembly.Instance}*/ let hbcInstance;
|
||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let hbcInstaceFuture;
|
||||
async function getHbcInstance() {
|
||||
hbcInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbc.wasm"), {});
|
||||
return hbcInstance ??= (await hbcInstaceFuture).instance;
|
||||
}
|
||||
|
||||
const stack_pointer_offset = 1 << 20;
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {Post[]} packages @param {number} fuel
|
||||
* @returns {string} */
|
||||
function compileCode(instance, packages, fuel = 100) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
LOG_MESSAGES, LOG_MESSAGES_LEN,
|
||||
memory, compile_and_run,
|
||||
} = instance.exports;
|
||||
|
||||
if (!(true
|
||||
&& memory instanceof WebAssembly.Memory
|
||||
&& INPUT instanceof WebAssembly.Global
|
||||
&& INPUT_LEN instanceof WebAssembly.Global
|
||||
&& LOG_MESSAGES instanceof WebAssembly.Global
|
||||
&& LOG_MESSAGES_LEN instanceof WebAssembly.Global
|
||||
&& typeof compile_and_run === "function"
|
||||
)) never();
|
||||
|
||||
const codeLength = packPosts(packages, new DataView(memory.buffer, INPUT.value));
|
||||
new DataView(memory.buffer).setUint32(INPUT_LEN.value, codeLength, true);
|
||||
|
||||
runWasmFunction(instance, compile_and_run, fuel);
|
||||
return bufToString(memory, LOG_MESSAGES, LOG_MESSAGES_LEN).trim();
|
||||
}
|
||||
|
||||
/**@type{WebAssembly.Instance}*/ let fmtInstance;
|
||||
/**@type{Promise<WebAssembly.WebAssemblyInstantiatedSource>}*/ let fmtInstaceFuture;
|
||||
async function getFmtInstance() {
|
||||
fmtInstaceFuture ??= WebAssembly.instantiateStreaming(fetch("/hbfmt.wasm"), {});
|
||||
return fmtInstance ??= (await fmtInstaceFuture).instance;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {string} code @param {"tok" | "fmt" | "minify"} action
|
||||
* @returns {string | Uint8Array | undefined} */
|
||||
function modifyCode(instance, code, action) {
|
||||
let {
|
||||
INPUT, INPUT_LEN,
|
||||
OUTPUT, OUTPUT_LEN,
|
||||
memory, fmt, tok, minify
|
||||
} = instance.exports;
|
||||
|
||||
let funs = { fmt, tok, minify };
|
||||
let fun = funs[action];
|
||||
if (!(true
|
||||
&& memory instanceof WebAssembly.Memory
|
||||
&& INPUT instanceof WebAssembly.Global
|
||||
&& INPUT_LEN instanceof WebAssembly.Global
|
||||
&& OUTPUT instanceof WebAssembly.Global
|
||||
&& OUTPUT_LEN instanceof WebAssembly.Global
|
||||
&& funs.hasOwnProperty(action)
|
||||
&& typeof fun === "function"
|
||||
)) never();
|
||||
|
||||
if (action !== "fmt") {
|
||||
INPUT = OUTPUT;
|
||||
INPUT_LEN = OUTPUT_LEN;
|
||||
}
|
||||
|
||||
let dw = new DataView(memory.buffer);
|
||||
dw.setUint32(INPUT_LEN.value, code.length, true);
|
||||
new Uint8Array(memory.buffer, INPUT.value).set(new TextEncoder().encode(code));
|
||||
|
||||
if (!runWasmFunction(instance, fun)) {
|
||||
return undefined;
|
||||
}
|
||||
if (action === "tok") {
|
||||
return bufSlice(memory, OUTPUT, OUTPUT_LEN);
|
||||
} else {
|
||||
return bufToString(memory, OUTPUT, OUTPUT_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @param {WebAssembly.Instance} instance @param {CallableFunction} func @param {any[]} args
|
||||
* @returns {boolean} */
|
||||
function runWasmFunction(instance, func, ...args) {
|
||||
const { PANIC_MESSAGE, PANIC_MESSAGE_LEN, memory, stack_pointer } = instance.exports;
|
||||
if (!(true
|
||||
&& memory instanceof WebAssembly.Memory
|
||||
&& stack_pointer instanceof WebAssembly.Global
|
||||
)) never();
|
||||
const ptr = stack_pointer.value;
|
||||
try {
|
||||
func(...args);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof WebAssembly.RuntimeError
|
||||
&& error.message == "unreachable"
|
||||
&& PANIC_MESSAGE instanceof WebAssembly.Global
|
||||
&& PANIC_MESSAGE_LEN instanceof WebAssembly.Global) {
|
||||
console.error(bufToString(memory, PANIC_MESSAGE, PANIC_MESSAGE_LEN), error);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
stack_pointer.value = ptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {Object} Post
|
||||
* @property {string} path
|
||||
* @property {string} code */
|
||||
|
||||
/** @param {Post[]} posts @param {DataView} view @returns {number} */
|
||||
function packPosts(posts, view) {
|
||||
const enc = new TextEncoder(), buf = new Uint8Array(view.buffer, view.byteOffset);
|
||||
let len = 0; for (const post of posts) {
|
||||
view.setUint16(len, post.path.length, true); len += 2;
|
||||
buf.set(enc.encode(post.path), len); len += post.path.length;
|
||||
view.setUint16(len, post.code.length, true); len += 2;
|
||||
buf.set(enc.encode(post.code), len); len += post.code.length;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Memory} mem
|
||||
* @param {WebAssembly.Global} ptr
|
||||
* @param {WebAssembly.Global} len
|
||||
* @return {Uint8Array} */
|
||||
function bufSlice(mem, ptr, len) {
|
||||
return new Uint8Array(mem.buffer, ptr.value,
|
||||
new DataView(mem.buffer).getUint32(len.value, true));
|
||||
}
|
||||
|
||||
/** @param {WebAssembly.Memory} mem
|
||||
* @param {WebAssembly.Global} ptr
|
||||
* @param {WebAssembly.Global} len
|
||||
* @return {string} */
|
||||
function bufToString(mem, ptr, len) {
|
||||
const res = new TextDecoder()
|
||||
.decode(new Uint8Array(mem.buffer, ptr.value,
|
||||
new DataView(mem.buffer).getUint32(len.value, true)));
|
||||
new DataView(mem.buffer).setUint32(len.value, 0, true);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function wireUp(target) {
|
||||
execApply(target);
|
||||
cacheInputs(target);
|
||||
bindCodeEdit(target);
|
||||
bindTextareaAutoResize(target);
|
||||
}
|
||||
|
||||
const importRe = /@use\s*\(\s*"(([^"]|\\")+)"\s*\)/g;
|
||||
|
||||
/** @param {WebAssembly.Instance} fmt
|
||||
* @param {string} code
|
||||
* @param {string[]} roots
|
||||
* @param {Post[]} buf
|
||||
* @param {Set<string>} prevRoots
|
||||
* @returns {void} */
|
||||
function loadCachedPackages(fmt, code, roots, buf, prevRoots) {
|
||||
buf[0].code = code;
|
||||
|
||||
roots.length = 0;
|
||||
let changed = false;
|
||||
for (const match of code.matchAll(importRe)) {
|
||||
changed ||= !prevRoots.has(match[1]);
|
||||
roots.push(match[1]);
|
||||
}
|
||||
|
||||
if (!changed) return;
|
||||
buf.length = 1;
|
||||
prevRoots.clear();
|
||||
|
||||
for (let imp = roots.pop(); imp !== undefined; imp = roots.pop()) {
|
||||
if (prevRoots.has(imp)) continue; prevRoots.add(imp);
|
||||
|
||||
const fmtd = modifyCode(fmt, localStorage.getItem("package-" + imp) ?? never(), "fmt");
|
||||
if (typeof fmtd != "string") never();
|
||||
buf.push({ path: imp, code: fmtd });
|
||||
for (const match of buf[buf.length - 1].code.matchAll(importRe)) {
|
||||
roots.push(match[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**@type{Set<string>}*/ const prevRoots = new Set();
|
||||
/**@typedef {Object} PackageCtx
|
||||
* @property {AbortController} [cancelation]
|
||||
* @property {string[]} keyBuf
|
||||
* @property {Set<string>} prevParams
|
||||
* @property {HTMLTextAreaElement} [edit] */
|
||||
|
||||
/** @param {string} source @param {Set<string>} importDiff @param {HTMLPreElement} errors @param {PackageCtx} ctx */
|
||||
async function fetchPackages(source, importDiff, errors, ctx) {
|
||||
importDiff.clear();
|
||||
for (const match of source.matchAll(importRe)) {
|
||||
if (localStorage["package-" + match[1]]) continue;
|
||||
importDiff.add(match[1]);
|
||||
}
|
||||
|
||||
if (importDiff.size !== 0 && (ctx.prevParams.size != importDiff.size
|
||||
|| [...ctx.prevParams.keys()].every(e => importDiff.has(e)))) {
|
||||
if (ctx.cancelation) ctx.cancelation.abort();
|
||||
ctx.prevParams.clear();
|
||||
ctx.prevParams = new Set([...importDiff]);
|
||||
ctx.cancelation = new AbortController();
|
||||
|
||||
ctx.keyBuf.length = 0;
|
||||
ctx.keyBuf.push(...importDiff.keys());
|
||||
|
||||
errors.textContent = "fetching: " + ctx.keyBuf.join(", ");
|
||||
|
||||
await fetch(`/code`, {
|
||||
method: "POST",
|
||||
signal: ctx.cancelation.signal,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(ctx.keyBuf),
|
||||
}).then(async e => {
|
||||
try {
|
||||
const json = await e.json();
|
||||
if (e.status == 200) {
|
||||
for (const [key, value] of Object.entries(json)) {
|
||||
localStorage["package-" + key] = value;
|
||||
}
|
||||
const missing = ctx.keyBuf.filter(i => json[i] === undefined);
|
||||
if (missing.length !== 0) {
|
||||
errors.textContent = "deps not found: " + missing.join(", ");
|
||||
} else {
|
||||
ctx.cancelation = undefined;
|
||||
ctx.edit?.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
}
|
||||
} catch (er) {
|
||||
errors.textContent = "completely failed to fetch ("
|
||||
+ e.status + "): " + ctx.keyBuf.join(", ");
|
||||
console.error(e, er);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
async function bindCodeEdit(target) {
|
||||
const edit = target.querySelector("#code-edit");
|
||||
if (!(edit instanceof HTMLTextAreaElement)) return;
|
||||
|
||||
const codeSize = target.querySelector("#code-size");
|
||||
const errors = target.querySelector("#compiler-output");
|
||||
if (!(true
|
||||
&& codeSize instanceof HTMLSpanElement
|
||||
&& errors instanceof HTMLPreElement
|
||||
)) never();
|
||||
|
||||
const MAX_CODE_SIZE = parseInt(codeSize.innerHTML);
|
||||
if (Number.isNaN(MAX_CODE_SIZE)) never();
|
||||
|
||||
const hbc = await getHbcInstance(), fmt = await getFmtInstance();
|
||||
let importDiff = new Set();
|
||||
/**@type{Post[]}*/
|
||||
const packages = [{ path: "local.hb", code: "" }];
|
||||
const debounce = 100;
|
||||
let timeout = 0;
|
||||
const ctx = { keyBuf: [], prevParams: new Set(), edit };
|
||||
|
||||
prevRoots.clear();
|
||||
|
||||
const onInput = () => {
|
||||
fetchPackages(edit.value, importDiff, errors, ctx);
|
||||
|
||||
if (ctx.cancelation && importDiff.size !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadCachedPackages(fmt, edit.value, ctx.keyBuf, packages, prevRoots);
|
||||
|
||||
errors.textContent = compileCode(hbc, packages);
|
||||
const minified_size = modifyCode(fmt, edit.value, "minify")?.length;
|
||||
if (minified_size) {
|
||||
codeSize.textContent = (MAX_CODE_SIZE - minified_size) + "";
|
||||
const perc = Math.min(100, Math.floor(100 * (minified_size / MAX_CODE_SIZE)));
|
||||
codeSize.style.color = `color-mix(in srgb, light-dark(black, white), var(--error) ${perc}%)`;
|
||||
}
|
||||
timeout = 0;
|
||||
};
|
||||
|
||||
edit.addEventListener("input", () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(onInput, debounce)
|
||||
});
|
||||
edit.dispatchEvent(new InputEvent("input"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Array<string>}
|
||||
* to be synched with `enum TokenGroup` in bytecode/src/fmt.rs */
|
||||
const TOK_CLASSES = [
|
||||
'Blank',
|
||||
'Comment',
|
||||
'Keyword',
|
||||
'Identifier',
|
||||
'Directive',
|
||||
'Number',
|
||||
'String',
|
||||
'Op',
|
||||
'Assign',
|
||||
'Paren',
|
||||
'Bracket',
|
||||
'Colon',
|
||||
'Comma',
|
||||
'Dot',
|
||||
'Ctor',
|
||||
];
|
||||
|
||||
/** @type {{ [key: string]: (el: HTMLElement) => void | Promise<void> }} */
|
||||
const applyFns = {
|
||||
timestamp: (el) => {
|
||||
const timestamp = el.innerText;
|
||||
const date = new Date(parseInt(timestamp) * 1000);
|
||||
el.innerText = date.toLocaleString();
|
||||
},
|
||||
fmt,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target */
|
||||
async function fmt(target) {
|
||||
const code = target.innerText;
|
||||
const instance = await getFmtInstance();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const fmt = modifyCode(instance, code, 'fmt');
|
||||
if (typeof fmt !== "string") return;
|
||||
const codeBytes = new TextEncoder().encode(fmt);
|
||||
const tok = modifyCode(instance, fmt, 'tok');
|
||||
if (!(tok instanceof Uint8Array)) return;
|
||||
target.innerHTML = '';
|
||||
let start = 0;
|
||||
let kind = tok[0];
|
||||
for (let ii = 1; ii <= tok.length; ii += 1) {
|
||||
// split over same tokens and buffer end
|
||||
if (tok[ii] === kind && ii < tok.length) {
|
||||
continue;
|
||||
}
|
||||
const text = decoder.decode(codeBytes.subarray(start, ii));
|
||||
const textNode = document.createTextNode(text);;
|
||||
if (kind === 0) {
|
||||
target.appendChild(textNode);
|
||||
} else {
|
||||
const el = document.createElement('span');
|
||||
el.classList.add('syn');
|
||||
el.classList.add(TOK_CLASSES[kind]);
|
||||
el.appendChild(textNode);
|
||||
target.appendChild(el);
|
||||
}
|
||||
if (ii == tok.length) {
|
||||
break;
|
||||
}
|
||||
start = ii;
|
||||
kind = tok[ii];
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function execApply(target) {
|
||||
const proises = [];
|
||||
for (const elem of target.querySelectorAll('[apply]')) {
|
||||
if (!(elem instanceof HTMLElement)) continue;
|
||||
const funcname = elem.getAttribute('apply') ?? never();
|
||||
const vl = applyFns[funcname](elem);
|
||||
if (vl instanceof Promise) proises.push(vl);
|
||||
}
|
||||
if (target === document.body) {
|
||||
Promise.all(proises).then(() => document.body.hidden = false);
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function bindTextareaAutoResize(target) {
|
||||
for (const textarea of target.querySelectorAll("textarea")) {
|
||||
if (!(textarea instanceof HTMLTextAreaElement)) never();
|
||||
|
||||
const taCssMap = window.getComputedStyle(textarea);
|
||||
const padding = parseInt(taCssMap.getPropertyValue('padding-top') ?? "0")
|
||||
+ parseInt(taCssMap.getPropertyValue('padding-top') ?? "0");
|
||||
textarea.style.height = "auto";
|
||||
textarea.style.height = (textarea.scrollHeight - padding) + "px";
|
||||
textarea.style.overflowY = "hidden";
|
||||
textarea.addEventListener("input", function() {
|
||||
let top = window.scrollY;
|
||||
textarea.style.height = "auto";
|
||||
textarea.style.height = (textarea.scrollHeight - padding) + "px";
|
||||
window.scrollTo({ top });
|
||||
});
|
||||
|
||||
textarea.onkeydown = (ev) => {
|
||||
if (ev.key === "Tab") {
|
||||
ev.preventDefault();
|
||||
document.execCommand('insertText', false, "\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function cacheInputs(target) {
|
||||
/**@type {HTMLFormElement}*/ let form;
|
||||
for (form of target.querySelectorAll('form')) {
|
||||
const path = form.getAttribute('hx-post') || form.getAttribute('hx-delete');
|
||||
if (!path) {
|
||||
console.warn('form does not have a hx-post or hx-delete attribute', form);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const input of form.elements) {
|
||||
if (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) {
|
||||
if ('password submit button'.includes(input.type)) continue;
|
||||
const key = path + input.name;
|
||||
input.value = localStorage.getItem(key) ?? '';
|
||||
input.addEventListener("input", () => localStorage.setItem(key, input.value));
|
||||
} else {
|
||||
console.warn("unhandled form element: ", input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} [path] */
|
||||
function updateTab(path) {
|
||||
console.log(path);
|
||||
for (const elem of document.querySelectorAll("button[hx-push-url]")) {
|
||||
if (elem instanceof HTMLButtonElement)
|
||||
elem.disabled =
|
||||
elem.getAttribute("hx-push-url") === path
|
||||
|| elem.getAttribute("hx-push-url") === window.location.pathname;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.hostname === 'localhost') {
|
||||
let id; setInterval(async () => {
|
||||
let new_id = await fetch('/hot-reload').then(reps => reps.text());
|
||||
id ??= new_id;
|
||||
if (id !== new_id) window.location.reload();
|
||||
}, 300);
|
||||
|
||||
(async function test() {
|
||||
{
|
||||
const code = "main:=fn():void{return}";
|
||||
const inst = await getFmtInstance()
|
||||
const fmtd = modifyCode(inst, code, "fmt") ?? never();
|
||||
if (typeof fmtd !== "string") never();
|
||||
const prev = modifyCode(inst, fmtd, "minify") ?? never();
|
||||
if (code != prev) console.error(code, prev);
|
||||
}
|
||||
{
|
||||
const posts = [{
|
||||
path: "foo.hb",
|
||||
code: "main:=fn():int{return 42}",
|
||||
}];
|
||||
const res = compileCode(await getHbcInstance(), posts, 1) ?? never();
|
||||
const expected = "exit code: 42";
|
||||
if (expected != res) console.error(expected, res);
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
document.body.addEventListener('htmx:afterSwap', (ev) => {
|
||||
if (!(ev.target instanceof HTMLElement)) never();
|
||||
wireUp(ev.target);
|
||||
if (ev.target.tagName == "MAIN" || ev.target.tagName == "BODY")
|
||||
updateTab(ev['detail'].pathInfo.finalRequestPath);
|
||||
});
|
||||
|
||||
getFmtInstance().then(inst => {
|
||||
document.body.addEventListener('htmx:configRequest', (ev) => {
|
||||
const details = ev['detail'];
|
||||
if (details.path === "/post" && details.verb === "post") {
|
||||
details.parameters['code'] = modifyCode(inst, details.parameters['code'], "minify");
|
||||
}
|
||||
});
|
||||
|
||||
/** @param {string} query @param {string} target @returns {number} */
|
||||
function fuzzyCost(query, target) {
|
||||
let qi = 0, bi = 0, cost = 0, matched = false;
|
||||
while (qi < query.length) {
|
||||
if (query.charAt(qi) === target.charAt(bi++)) {
|
||||
matched = true;
|
||||
qi++;
|
||||
} else {
|
||||
cost++;
|
||||
}
|
||||
if (bi === target.length) (bi = 0, qi++);
|
||||
}
|
||||
return cost + (matched ? 0 : 100 * target.length);
|
||||
}
|
||||
|
||||
let deps = undefined;
|
||||
/** @param {HTMLInputElement} input @returns {void} */
|
||||
function filterCodeDeps(input) {
|
||||
deps ??= document.getElementById("deps");
|
||||
if (!(deps instanceof HTMLElement)) never();
|
||||
if (input.value === "") {
|
||||
deps.textContent = "results show here...";
|
||||
return;
|
||||
}
|
||||
deps.innerHTML = "";
|
||||
for (const root of [...prevRoots.keys()]
|
||||
.sort((a, b) => fuzzyCost(input.value, a) - fuzzyCost(input.value, b))) {
|
||||
const pane = document.createElement("div");
|
||||
const code = modifyCode(inst, localStorage["package-" + root], "fmt");
|
||||
pane.innerHTML = `<div>${root}</div><pre>${code}</pre>`;
|
||||
deps.appendChild(pane);
|
||||
}
|
||||
if (deps.innerHTML === "") {
|
||||
deps.textContent = "no results";
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(window, { filterCodeDeps });
|
||||
});
|
||||
|
||||
/** @param {HTMLElement} target */
|
||||
function runPost(target) {
|
||||
while (!target.matches("div[class=preview]")) target = target.parentElement ?? never();
|
||||
const code = target.querySelector("pre[apply=fmt]");
|
||||
if (!(code instanceof HTMLPreElement)) never();
|
||||
const output = target.querySelector("pre[id=compiler-output]");
|
||||
if (!(output instanceof HTMLPreElement)) never();
|
||||
|
||||
Promise.all([getHbcInstance(), getFmtInstance()]).then(async ([hbc, fmt]) => {
|
||||
const ctx = { keyBuf: [], prevParams: new Set() };
|
||||
await fetchPackages(code.innerText ?? never(), new Set(), output, ctx);
|
||||
const posts = [{ path: "this", code: "" }];
|
||||
loadCachedPackages(fmt, code.innerText ?? never(), ctx.keyBuf, posts, new Set());
|
||||
output.textContent = compileCode(hbc, posts);
|
||||
output.hidden = false;
|
||||
});
|
||||
|
||||
let author = encodeURIComponent(target.dataset.author ?? never());
|
||||
let name = encodeURIComponent(target.dataset.name ?? never());
|
||||
fetch(`/post/run?author=${author}&name=${name}`, { method: "POST" })
|
||||
}
|
||||
|
||||
Object.assign(window, { runPost });
|
||||
|
||||
updateTab();
|
||||
wireUp(document.body);
|
||||
|
1040
depell/src/main.rs
1040
depell/src/main.rs
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user(
|
||||
name TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
PRIMARY KEY (name)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session(
|
||||
id BLOB NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
expiration INTEGER NOT NULL,
|
||||
FOREIGN KEY (username) REFERENCES user (name)
|
||||
PRIMARY KEY (username)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS
|
||||
session_id ON session (id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS post(
|
||||
name TEXT NOT NULL,
|
||||
author TEXT,
|
||||
timestamp INTEGER,
|
||||
code TEXT NOT NULL,
|
||||
FOREIGN KEY (author) REFERENCES user(name) ON DELETE SET NULL,
|
||||
PRIMARY KEY (author, name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS
|
||||
post_timestamp ON post(timestamp DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS import(
|
||||
from_name TEXT NOT NULL,
|
||||
from_author TEXT,
|
||||
to_name TEXT NOT NULL,
|
||||
to_author TEXT,
|
||||
FOREIGN KEY (from_name, from_author) REFERENCES post(name, author),
|
||||
FOREIGN KEY (to_name, to_author) REFERENCES post(name, author)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS
|
||||
dependencies ON import(from_name, from_author);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS
|
||||
dependants ON import(to_name, to_author);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS run(
|
||||
code_name TEXT NOT NULL,
|
||||
code_author TEXT NOT NULL,
|
||||
runner TEXT NOT NULL,
|
||||
FOREIGN KEY (code_name, code_author) REFERENCES post(name, author),
|
||||
FOREIGN KEY (runner) REFERENCES user(name),
|
||||
PRIMARY KEY (code_name, code_author, runner)
|
||||
);
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
# The journey to an optimizing compiler
|
||||
|
||||
It's been years since I was continuously trying to make a compiler to implement language of my dreams. Problem was tho that I wanted something similar to Rust, which if you did not know, `rustc` far exceeded the one million lines of code mark some time ago, so implementing such language would take me years if not decades, but I still tired it.
|
||||
|
||||
Besides being extremely ambitions, the problem with my earliest attempts at making a compiler, is that literally nobody, not even me, was using the language, and so retroactively I am confident, what I implemented was a complex test-case implementation, and not a compiler. I often fall into a trap of implementing edge cases instead of an algorithm that would handle not only the very few thing the tests do but also all the other stuff that users of the language would try.
|
||||
|
||||
Another part of why I was failing for all that time, is that I did the hardest thing first without understanding the core concepts involved in translating written language to IR, god forbid assembly. I wasted a lot of time like this, but at least I learned Rust well. At some point I found a job where I started developing a decentralized network and that fully drawn me away from language development.
|
||||
|
||||
## Completely new approach
|
||||
|
||||
At some point the company I was working for started having financial issues and they were unable to pay me. During that period, I discovered that my love for networking was majorly fueled by the monetary gains associated with it. I burned out, and started to look for things to do with the free time.
|
||||
|
||||
One could say timing was perfect because [`ableos`](https://git.ablecorp.us/AbleOS/ableos) was desperately in need of a sane programming language that compiles to the home made VM ISA used for all software ran in `ableos`, but there was nobody crazy enough to do this. I got terribly nerd sniped, tho I don't regret it. Process of making a language for `ableos` was completely different. Firstly, it needed to be done asap, the lack of a good language blocked everyone form writing drivers for `ableos`, secondly, the moment the language is at least a little bit usable, people other then me will start using it, and lastly, the ISA the language compiles to very simple to emit, understand, and run.
|
||||
|
||||
### Urgency is a bliss
|
||||
|
||||
I actually managed to make the language somewhat work in one week, mainly because my mind set changed. I no longer spent a lot of time designing syntax for elegance, I designed it so that it incredibly easy to parse, meaning I can spent minimal effort implementing the parser, and fully focus on the hard problem of translating AST to instructions. Surprisingly, making everything an expression and not enforcing any arbitrary rules, makes the code you can write incredibly flexible and (most) people love it. One of the decisions I made to save time (or maybe it was an accident) was to make `,;` not enforced, meaning, you are allowed to write delimiters in lists but, as long as it does not change the intent of the code, you can leave them out. In practice, you actually don't need semicolons, unless the next line starts with something sticky like `*x`, int that case you put a semicolon on the previous line to tell the parser where the current expression ends.
|
||||
|
||||
### Only the problem I care about
|
||||
|
||||
Its good to note that writing a parser is no longer interesting for me. I wrote many parsers before and writing one no longer feel rewarding, but more like a chore. The real problem I was excited about was translating AST to instructions, I always ended up overcomplicating this step wit edge cases for every possible scenario that can happen in code, for which there are infinite. But why did I succeed this time? Well all the friction related to getting something that I can execute was so low, I could iterate quickly and realize what I am doing wrong before I burn out. In a week I managed to understand what I was failing to do for years, partly because of all the previous suffering, but mainly because it was so easy to pivot and try new things. And so I managed to make my first single pass compiler, and people immediately started using it.
|
||||
|
||||
### Don't implement features nobody asked for
|
||||
|
||||
Immediately after someone else then me wrote something in `hb` stuff started breaking, over the course of a month I kept fixing bugs and adding new features just fine, and more people started to use the language. All was good and well until I looked into the code. It was incredibly cursed, full of tricks to work around the compiler not doing any optimizations. At that moment I realized the whole compiler after parser needs to be rewritten, I had to implement optimizations, otherwise people wont be able to write readable code that runs fast. All of the features I have added up until now, were a technical dept now. Unless they are all working with optimizations, can't compile the existing code. Yes, if feature exists, be sure as hell it will be used.
|
||||
|
||||
It took around 4 months to reimplement everything make make the optimal code look like what you are used to in other languages. I am really thankful for [sea of nodes](https://github.com/SeaOfNodes), and all the amazing work Cliff Click and others do to make demystify optimizers, It would have taken much longer to for me to figure all the principles out without the exhaustive [tutorial](https://github.com/SeaOfNodes/Simple?tab=readme-ov-file).
|
||||
|
||||
## How my understanding of optimizations changed
|
||||
|
||||
### Optimizations allow us to scale software
|
||||
|
||||
I need to admit, before writing a single pass compiler and later upgrading it to optimizing one, I thought optimizations only affect the quality of final assembly emitted by the compiler. It never occur to me that what the optimizations actually do, is reduce the impact of how you decide to write the code. In a single pass compiler (with zero optimizations), the machine code reflects:
|
||||
|
||||
- order of operations as written in code
|
||||
- whether the value was stored in intermediate locations
|
||||
- exact structure of the control flow and at which point the operations are placed
|
||||
- how many times is something recomputed
|
||||
- operations that only help to convey intent for the reader of the source code
|
||||
- and more I can't think of...
|
||||
|
||||
If you took some code you wrote and then modified it to obfuscate these aspects (in reference to the original code), you would to a subset of what optimizing compiler does. Of course, a good compiler would try hard to improve the metrics its optimizing for, it would:
|
||||
|
||||
- reorder operations to allow the CPU to parallelize them
|
||||
- remove needless stores, or store values directly to places you cant express in code
|
||||
- pull operations out of the loops and into the branches (if it can)
|
||||
- find all common sub-expressions and compute them only once
|
||||
- fold constants as much as possible and use obscure tricks to replace slow instructions if any of the operands are constant
|
||||
- and more...
|
||||
|
||||
In the end, compiler optimizations try to reduce correlation between how the code happens to be written and how well it performs, which is extremely important when you want humans to read the code.
|
||||
|
||||
### Optimizing compilers know more then you
|
||||
|
||||
Optimizing code is a search problem, an optimizer searches the code for patterns that can be rewritten so something more practical for the computer, while preserving the observable behavior of the program. This means it needs enough context about the code to not make a mistake. In fact, the optimizer has so much context, it is able to determine your code is useless. But wait, didn't you write the code because you needed it to do something? Maybe your intention was to break out of the loop after you are done, but the optimizer looked at the code and said, "great, we are so lucky that this integer is always small enough to miss this check by one, DELETE", and then he goes "jackpot, since this loop is now infinite, we don't need this code after it, DELETE". Notice that the optimizer is eager to delete dead code, it did not ask you "Brah, why did you place all your code after an infinite loop?". This is just an example, there are many more cases where modern optimizers just delete all your code because they proven it does something invalid without running it.
|
||||
|
||||
Its stupid but its the world we live in, optimizers are usually a black box you import and feed it the code in a format they understand, they then proceed to optimize it, and if they find a glaring bug they wont tell you, god forbid, they will just molest the code in unspecified ways and spit out whats left. Before writing an optimizer, I did no know this can happen and I did not know this is a problem I pay for with my time, spent figuring out why noting is happening when I run the program.
|
||||
|
||||
But wait its worse! Since optimizers wont ever share the fact you are stupid, we end up with other people painstakingly writing complex linters, that will do a shitty job detecting things that matter, and instead whine about style and other bullcrap (and they suck even at that). If the people who write linters and people who write optimizers swapped the roles, I would be ranting about optimizers instead.
|
||||
|
||||
And so, this is the area where I want to innovate, lets report the dead code to the frontend, and let the compiler frontend filter out the noise and show relevant information in the diagnostics. Refuse to compile the program if you `i /= 0`. Refuse to compile if you `arr[arr.len]`. This is the level of stupid optimizer sees, once it normalizes your code, but proceeds to protect your feelings. My goal so for hblang to relay this to you as much as possible. If we can query for optimizations, we can query for bugs too.
|
|
@ -1,8 +0,0 @@
|
|||
### About posting code
|
||||
|
||||
If you are unfammiliar with [hblang](https://git.ablecorp.us/AbleOS/holey-bytes), refer to the **hblang/README.md** or vizit [mlokis'es posts](/profile/mlokis). Preferably don't edit the code here.
|
||||
|
||||
### Extra textarea features
|
||||
|
||||
- proper tab behaviour
|
||||
- snap to previous tab boundary on "empty" lines
|
|
@ -1,11 +0,0 @@
|
|||
## Welcome to depell
|
||||
|
||||
Depell (dependency hell) is a simple "social" media site, except that all you can post is [hblang](https://git.ablecorp.us/AbleOS/holey-bytes) code. Instead of likes you run the program, and instead of mentions you import the program as dependency. Run counts even when ran indirectly.
|
||||
|
||||
The backend only serves the code and frontend compiles and runs it locally. All posts are immutable.
|
||||
|
||||
## Security?
|
||||
|
||||
All code runs in WASM (inside a holey-bytes VM until hblang compiles to wasm) and is controlled by JavaScript. WASM
|
||||
cant do any form of IO without going trough JavaScript so as long as JS import does not allow wasm to execute
|
||||
arbitrary JS code, WASM can act as a container inside the JS.
|
|
@ -1,11 +0,0 @@
|
|||
[package]
|
||||
name = "wasm-hbfmt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
hblang = { workspace = true, features = ["no_log"] }
|
||||
wasm-rt = { version = "0.1.0", path = "../wasm-rt" }
|
|
@ -1,42 +0,0 @@
|
|||
#![no_std]
|
||||
#![feature(str_from_raw_parts)]
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use hblang::{fmt, parser};
|
||||
|
||||
wasm_rt::decl_runtime!(128 * 1024, 1024 * 4);
|
||||
|
||||
const MAX_OUTPUT_SIZE: usize = 1024 * 10;
|
||||
wasm_rt::decl_buffer!(MAX_OUTPUT_SIZE, MAX_OUTPUT, OUTPUT, OUTPUT_LEN);
|
||||
|
||||
const MAX_INPUT_SIZE: usize = 1024 * 4;
|
||||
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fmt() {
|
||||
ALLOCATOR.reset();
|
||||
|
||||
let code = core::str::from_raw_parts(core::ptr::addr_of!(INPUT).cast(), INPUT_LEN);
|
||||
|
||||
let arena = parser::Arena::with_capacity(code.len() * parser::SOURCE_TO_AST_FACTOR);
|
||||
let mut ctx = parser::Ctx::default();
|
||||
let exprs = parser::Parser::parse(&mut ctx, code, "source.hb", &mut parser::no_loader, &arena);
|
||||
|
||||
let mut f = wasm_rt::Write(&mut OUTPUT[..]);
|
||||
fmt::fmt_file(exprs, code, &mut f).unwrap();
|
||||
OUTPUT_LEN = MAX_OUTPUT_SIZE - f.0.len();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tok() {
|
||||
let code = core::slice::from_raw_parts_mut(
|
||||
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||
OUTPUT_LEN = fmt::get_token_kinds(code);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn minify() {
|
||||
let code = core::str::from_raw_parts_mut(
|
||||
core::ptr::addr_of_mut!(OUTPUT).cast(), OUTPUT_LEN);
|
||||
OUTPUT_LEN = fmt::minify(code);
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "wasm-hbc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
hblang = { workspace = true, features = [] }
|
||||
hbvm.workspace = true
|
||||
log = { version = "0.4.22", features = ["release_max_level_error"] }
|
||||
wasm-rt = { version = "0.1.0", path = "../wasm-rt", features = ["log"] }
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
#![feature(alloc_error_handler)]
|
||||
#![feature(slice_take)]
|
||||
#![no_std]
|
||||
|
||||
use {
|
||||
alloc::{string::String, vec::Vec},
|
||||
core::ffi::CStr,
|
||||
hblang::{
|
||||
backend::hbvm::HbvmBackend,
|
||||
son::{Codegen, CodegenCtx},
|
||||
ty::Module,
|
||||
utils::Ent,
|
||||
},
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
const ARENA_CAP: usize = 128 * 16 * 1024;
|
||||
wasm_rt::decl_runtime!(ARENA_CAP, 1024 * 4);
|
||||
|
||||
const MAX_INPUT_SIZE: usize = 32 * 4 * 1024;
|
||||
wasm_rt::decl_buffer!(MAX_INPUT_SIZE, MAX_INPUT, INPUT, INPUT_LEN);
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn compile_and_run(mut fuel: usize) {
|
||||
ALLOCATOR.reset();
|
||||
|
||||
_ = log::set_logger(&wasm_rt::Logger);
|
||||
log::set_max_level(log::LevelFilter::Error);
|
||||
|
||||
struct File<'a> {
|
||||
path: &'a str,
|
||||
code: &'a mut str,
|
||||
}
|
||||
|
||||
let mut root = 0;
|
||||
|
||||
let files = {
|
||||
let mut input_bytes =
|
||||
core::slice::from_raw_parts_mut(core::ptr::addr_of_mut!(INPUT).cast::<u8>(), INPUT_LEN);
|
||||
|
||||
let mut files = Vec::with_capacity(32);
|
||||
while let Some((&mut path_len, rest)) = input_bytes.split_first_chunk_mut() {
|
||||
let (path, rest) = rest.split_at_mut(u16::from_le_bytes(path_len) as usize);
|
||||
let (&mut code_len, rest) = rest.split_first_chunk_mut().unwrap();
|
||||
let (code, rest) = rest.split_at_mut(u16::from_le_bytes(code_len) as usize);
|
||||
files.push(File {
|
||||
path: core::str::from_utf8_unchecked(path),
|
||||
code: core::str::from_utf8_unchecked_mut(code),
|
||||
});
|
||||
input_bytes = rest;
|
||||
}
|
||||
|
||||
let root_path = files[root].path;
|
||||
hblang::quad_sort(&mut files, |a, b| a.path.cmp(b.path));
|
||||
root = files.binary_search_by_key(&root_path, |p| p.path).unwrap();
|
||||
|
||||
files
|
||||
};
|
||||
|
||||
let mut ctx = CodegenCtx::default();
|
||||
|
||||
let files = {
|
||||
let paths = files.iter().map(|f| f.path).collect::<Vec<_>>();
|
||||
let mut loader = |path: &str, _: &str, kind| match kind {
|
||||
hblang::parser::FileKind::Module => Ok(paths.binary_search(&path).unwrap()),
|
||||
hblang::parser::FileKind::Embed => Err("embeds are not supported".into()),
|
||||
};
|
||||
files
|
||||
.into_iter()
|
||||
.map(|f| {
|
||||
hblang::parser::Ast::new(
|
||||
f.path,
|
||||
// since 'free' does nothing this is fine
|
||||
String::from_raw_parts(f.code.as_mut_ptr(), f.code.len(), f.code.len()),
|
||||
&mut ctx.parser,
|
||||
&mut loader,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let mut ct = {
|
||||
let mut backend = HbvmBackend::default();
|
||||
Codegen::new(&mut backend, &files, &mut ctx).generate(Module::new(root));
|
||||
|
||||
if !ctx.parser.errors.borrow().is_empty() {
|
||||
log::error!("{}", ctx.parser.errors.borrow());
|
||||
return;
|
||||
}
|
||||
|
||||
let mut c = Codegen::new(&mut backend, &files, &mut ctx);
|
||||
c.assemble_comptime()
|
||||
};
|
||||
|
||||
while fuel != 0 {
|
||||
match ct.vm.run() {
|
||||
Ok(hbvm::VmRunOk::End) => {
|
||||
log::error!("exit code: {}", ct.vm.read_reg(1).0 as i64);
|
||||
break;
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Ecall) => {
|
||||
let kind = ct.vm.read_reg(2).0;
|
||||
match kind {
|
||||
0 => {
|
||||
let str = ct.vm.read_reg(3).0;
|
||||
let str = unsafe { CStr::from_ptr(str as _) };
|
||||
log::error!("{}", str.to_str().unwrap());
|
||||
}
|
||||
unknown => log::error!("unknown ecall: {unknown}"),
|
||||
}
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Timer) => {
|
||||
fuel -= 1;
|
||||
if fuel == 0 {
|
||||
log::error!("program timed out");
|
||||
}
|
||||
}
|
||||
Ok(hbvm::VmRunOk::Breakpoint) => todo!(),
|
||||
Err(e) => {
|
||||
log::error!("vm error: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//log::error!("memory consumption: {}b / {}b", ALLOCATOR.used(), ARENA_CAP);
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "wasm-rt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log = { version = "0.4.22", optional = true }
|
|
@ -1,162 +0,0 @@
|
|||
#![feature(alloc_error_handler)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(slice_take)]
|
||||
#![no_std]
|
||||
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cell::UnsafeCell,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decl_buffer {
|
||||
($cap:expr, $export_cap:ident, $export_base:ident, $export_len:ident) => {
|
||||
#[no_mangle]
|
||||
static $export_cap: usize = $cap;
|
||||
#[no_mangle]
|
||||
static mut $export_base: [u8; $cap] = [0; $cap];
|
||||
#[no_mangle]
|
||||
static mut $export_len: usize = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! decl_runtime {
|
||||
($memory_size:expr, $max_panic_size:expr) => {
|
||||
#[cfg(debug_assertions)]
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE: [u8; $max_panic_size] = [0; $max_panic_size];
|
||||
#[cfg(debug_assertions)]
|
||||
#[no_mangle]
|
||||
static mut PANIC_MESSAGE_LEN: usize = 0;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[panic_handler]
|
||||
pub fn handle_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unsafe {
|
||||
use core::fmt::Write;
|
||||
let mut f = $crate::Write(&mut PANIC_MESSAGE[..]);
|
||||
_ = writeln!(f, "{}", _info);
|
||||
PANIC_MESSAGE_LEN = $max_panic_size - f.0.len();
|
||||
}
|
||||
}
|
||||
|
||||
core::arch::wasm32::unreachable();
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: $crate::ArenaAllocator<{ $memory_size }> = $crate::ArenaAllocator::new();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unsafe {
|
||||
use core::fmt::Write;
|
||||
let mut f = $crate::Write(&mut PANIC_MESSAGE[..]);
|
||||
_ = writeln!(f, "out of memory");
|
||||
PANIC_MESSAGE_LEN = $max_panic_size - f.0.len();
|
||||
}
|
||||
}
|
||||
|
||||
core::arch::wasm32::unreachable()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
pub struct Logger;
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, _: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
const MAX_LOG_MESSAGE: usize = 1024 * 8;
|
||||
#[no_mangle]
|
||||
static mut LOG_MESSAGES: [u8; MAX_LOG_MESSAGE] = [0; MAX_LOG_MESSAGE];
|
||||
#[no_mangle]
|
||||
static mut LOG_MESSAGES_LEN: usize = 0;
|
||||
|
||||
unsafe {
|
||||
use core::fmt::Write;
|
||||
let mut f = Write(&mut LOG_MESSAGES[LOG_MESSAGES_LEN..]);
|
||||
_ = writeln!(f, "{}", record.args());
|
||||
LOG_MESSAGES_LEN = MAX_LOG_MESSAGE - f.0.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
pub struct ArenaAllocator<const SIZE: usize> {
|
||||
arena: UnsafeCell<[u8; SIZE]>,
|
||||
head: UnsafeCell<*mut u8>,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> ArenaAllocator<SIZE> {
|
||||
#[expect(clippy::new_without_default)]
|
||||
pub const fn new() -> Self {
|
||||
ArenaAllocator {
|
||||
arena: UnsafeCell::new([0; SIZE]),
|
||||
head: UnsafeCell::new(core::ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::missing_safety_doc)]
|
||||
pub unsafe fn reset(&self) {
|
||||
(*self.head.get()) = self.arena.get().cast::<u8>().add(SIZE);
|
||||
}
|
||||
|
||||
pub fn used(&self) -> usize {
|
||||
unsafe { self.arena.get() as usize + SIZE - (*self.head.get()) as usize }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<const SIZE: usize> Sync for ArenaAllocator<SIZE> {}
|
||||
|
||||
unsafe impl<const SIZE: usize> GlobalAlloc for ArenaAllocator<SIZE> {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let size = layout.size();
|
||||
let align = layout.align();
|
||||
|
||||
let until = self.arena.get() as *mut u8;
|
||||
|
||||
let new_head = (*self.head.get()).sub(size);
|
||||
let aligned_head = (new_head as usize & !(align - 1)) as *mut u8;
|
||||
debug_assert!(aligned_head.is_aligned_to(align));
|
||||
|
||||
if until > aligned_head {
|
||||
return core::ptr::null_mut();
|
||||
}
|
||||
|
||||
*self.head.get() = aligned_head;
|
||||
aligned_head
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
||||
/* lol */
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Write<'a>(pub &'a mut [u8]);
|
||||
|
||||
impl core::fmt::Write for Write<'_> {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
if let Some(m) = self.0.take_mut(..s.len()) {
|
||||
m.copy_from_slice(s.as_bytes());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(core::fmt::Error)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#!/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
|
|
@ -5,6 +5,5 @@ edition = "2018"
|
|||
|
||||
[features]
|
||||
default = ["disasm"]
|
||||
disasm = ["alloc"]
|
||||
alloc = []
|
||||
|
||||
std = []
|
||||
disasm = ["std"]
|
|
@ -14,7 +14,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
|
||||
fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>> {
|
||||
writeln!(generated, "#![expect(dead_code)]")?;
|
||||
writeln!(generated, "#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]")?;
|
||||
writeln!(generated, "use crate::*;")?;
|
||||
|
||||
'_opcode_structs: {
|
||||
|
@ -85,7 +85,11 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
|
|||
}
|
||||
|
||||
'_name_list: {
|
||||
writeln!(generated, "pub const COUNT: u8 = {};", instructions().count())?;
|
||||
writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?;
|
||||
for [_, name, _, _] in instructions() {
|
||||
writeln!(generated, " \"{}\",", name.to_lowercase())?;
|
||||
}
|
||||
writeln!(generated, "];")?;
|
||||
}
|
||||
|
||||
let instr = "Instr";
|
||||
|
@ -98,27 +102,6 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
|
|||
writeln!(generated, " {name} = {id},")?;
|
||||
}
|
||||
writeln!(generated, "}}")?;
|
||||
|
||||
writeln!(generated, "impl {instr} {{")?;
|
||||
writeln!(generated, " pub fn size(self) -> usize {{")?;
|
||||
writeln!(generated, " match self {{")?;
|
||||
let mut instrs = instructions().collect::<Vec<_>>();
|
||||
instrs.sort_unstable_by_key(|&[.., ty, _]| iter_args(ty).map(arg_to_width).sum::<usize>());
|
||||
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| {
|
||||
iter_args(a).map(arg_to_width).sum::<usize>()
|
||||
== iter_args(b).map(arg_to_width).sum::<usize>()
|
||||
}) {
|
||||
let ty = group[0][2];
|
||||
for &[_, name, ..] in group {
|
||||
writeln!(generated, " | {instr}::{name}")?;
|
||||
}
|
||||
generated.pop();
|
||||
let size = iter_args(ty).map(arg_to_width).sum::<usize>() + 1;
|
||||
writeln!(generated, " => {size},")?;
|
||||
}
|
||||
writeln!(generated, " }}")?;
|
||||
writeln!(generated, " }}")?;
|
||||
writeln!(generated, "}}")?;
|
||||
}
|
||||
|
||||
'_arg_kind: {
|
||||
|
@ -140,7 +123,7 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>>
|
|||
"/// This assumes the instruction byte is still at the beginning of the buffer"
|
||||
)?;
|
||||
writeln!(generated, "#[cfg(feature = \"disasm\")]")?;
|
||||
writeln!(generated, "pub fn parse_args(bytes: &mut &[u8], kind: {instr}, buf: &mut alloc::vec::Vec<{oper}>) -> Option<()> {{")?;
|
||||
writeln!(generated, "pub fn parse_args(bytes: &mut &[u8], kind: {instr}, buf: &mut std::vec::Vec<{oper}>) -> Option<()> {{")?;
|
||||
writeln!(generated, " match kind {{")?;
|
||||
let mut instrs = instructions().collect::<Vec<_>>();
|
||||
instrs.sort_unstable_by_key(|&[.., ty, _]| ty);
|
||||
|
@ -180,7 +163,7 @@ fn comma_sep(items: impl Iterator<Item = String>) -> String {
|
|||
}
|
||||
|
||||
fn instructions() -> impl Iterator<Item = [&'static str; 4]> {
|
||||
include_str!("instructions.in")
|
||||
include_str!("../hbbytecode/instructions.in")
|
||||
.lines()
|
||||
.filter_map(|line| line.strip_suffix(';'))
|
||||
.map(|line| line.splitn(4, ',').map(str::trim).next_chunk().unwrap())
|
|
@ -1,7 +1,7 @@
|
|||
#![no_std]
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
pub use crate::instrs::*;
|
||||
use core::convert::TryFrom;
|
||||
|
@ -34,8 +34,8 @@ impl TryFrom<u8> for Instr {
|
|||
Err(value)
|
||||
}
|
||||
|
||||
if value < COUNT {
|
||||
unsafe { Ok(core::mem::transmute::<u8, Instr>(value)) }
|
||||
if value < NAMES.len() as u8 {
|
||||
unsafe { Ok(std::mem::transmute::<u8, Instr>(value)) }
|
||||
} else {
|
||||
failed(value)
|
||||
}
|
||||
|
@ -50,9 +50,8 @@ unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "disasm")]
|
||||
fn decode<T>(binary: &mut &[u8]) -> Option<T> {
|
||||
let (front, rest) = core::mem::take(binary).split_at_checked(core::mem::size_of::<T>())?;
|
||||
let (front, rest) = std::mem::take(binary).split_at_checked(core::mem::size_of::<T>())?;
|
||||
*binary = rest;
|
||||
unsafe { Some(core::ptr::read(front.as_ptr() as *const T)) }
|
||||
}
|
||||
|
@ -83,74 +82,41 @@ pub enum DisasmItem {
|
|||
}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
#[derive(Debug)]
|
||||
pub enum DisasmError<'a> {
|
||||
InvalidInstruction(u8),
|
||||
InstructionOutOfBounds(&'a str),
|
||||
FmtFailed(core::fmt::Error),
|
||||
HasOutOfBoundsJumps,
|
||||
}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
impl From<core::fmt::Error> for DisasmError<'_> {
|
||||
fn from(value: core::fmt::Error) -> Self {
|
||||
Self::FmtFailed(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
impl core::fmt::Display for DisasmError<'_> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match *self {
|
||||
DisasmError::InvalidInstruction(b) => write!(f, "invalid instruction opcode: {b}"),
|
||||
DisasmError::InstructionOutOfBounds(name) => {
|
||||
write!(f, "instruction would go out of bounds of {name} symbol")
|
||||
}
|
||||
DisasmError::FmtFailed(error) => write!(f, "fmt failed: {error}"),
|
||||
DisasmError::HasOutOfBoundsJumps => write!(
|
||||
f,
|
||||
"the code contained jumps that dont got neither to a \
|
||||
valid symbol or local insturction"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
impl core::error::Error for DisasmError<'_> {}
|
||||
|
||||
#[cfg(feature = "disasm")]
|
||||
pub fn disasm<'a>(
|
||||
pub fn disasm(
|
||||
binary: &mut &[u8],
|
||||
functions: &alloc::collections::BTreeMap<u32, (&'a str, u32, DisasmItem)>,
|
||||
out: &mut alloc::string::String,
|
||||
functions: &std::collections::BTreeMap<u32, (&str, u32, DisasmItem)>,
|
||||
out: &mut impl std::io::Write,
|
||||
mut eca_handler: impl FnMut(&mut &[u8]),
|
||||
) -> Result<(), DisasmError<'a>> {
|
||||
) -> std::io::Result<()> {
|
||||
use {
|
||||
self::instrs::Instr,
|
||||
alloc::{
|
||||
collections::btree_map::{BTreeMap, Entry},
|
||||
std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
convert::TryInto,
|
||||
vec::Vec,
|
||||
},
|
||||
core::{convert::TryInto, fmt::Write},
|
||||
};
|
||||
|
||||
fn instr_from_byte(b: u8) -> Result<Instr, DisasmError<'static>> {
|
||||
b.try_into().map_err(DisasmError::InvalidInstruction)
|
||||
fn instr_from_byte(b: u8) -> std::io::Result<Instr> {
|
||||
if b as usize >= instrs::NAMES.len() {
|
||||
return Err(std::io::ErrorKind::InvalidData.into());
|
||||
}
|
||||
Ok(unsafe { std::mem::transmute::<u8, Instr>(b) })
|
||||
}
|
||||
|
||||
let mut labels = BTreeMap::<u32, u32>::default();
|
||||
let mut labels = HashMap::<u32, u32>::default();
|
||||
let mut buf = Vec::<instrs::Oper>::new();
|
||||
let mut has_cycle = false;
|
||||
let mut has_oob = false;
|
||||
|
||||
'_offset_pass: for (&off, &(name, len, kind)) in functions.iter() {
|
||||
'_offset_pass: for (&off, &(_name, len, kind)) in functions.iter() {
|
||||
if matches!(kind, DisasmItem::Global) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let prev = *binary;
|
||||
|
||||
*binary = &binary[off as usize..];
|
||||
*binary = &binary[..off as usize];
|
||||
|
||||
let mut label_count = 0;
|
||||
while let Some(&byte) = binary.first() {
|
||||
|
@ -159,8 +125,7 @@ pub fn disasm<'a>(
|
|||
break;
|
||||
}
|
||||
let Ok(inst) = instr_from_byte(byte) else { break };
|
||||
instrs::parse_args(binary, inst, &mut buf)
|
||||
.ok_or(DisasmError::InstructionOutOfBounds(name))?;
|
||||
instrs::parse_args(binary, inst, &mut buf).ok_or(std::io::ErrorKind::OutOfMemory)?;
|
||||
|
||||
for op in buf.drain(..) {
|
||||
let rel = match op {
|
||||
|
@ -169,6 +134,8 @@ pub fn disasm<'a>(
|
|||
_ => continue,
|
||||
};
|
||||
|
||||
has_cycle |= rel == 0;
|
||||
|
||||
let global_offset: u32 = (offset + rel).try_into().unwrap();
|
||||
if functions.get(&global_offset).is_some() {
|
||||
continue;
|
||||
|
@ -201,7 +168,7 @@ pub fn disasm<'a>(
|
|||
|
||||
writeln!(out, "{name}:")?;
|
||||
|
||||
*binary = &binary[off as usize..];
|
||||
*binary = &binary[..off as usize];
|
||||
while let Some(&byte) = binary.first() {
|
||||
let offset: i32 = (prev.len() - binary.len()).try_into().unwrap();
|
||||
if offset as u32 == off + len {
|
||||
|
@ -252,9 +219,8 @@ pub fn disasm<'a>(
|
|||
} else {
|
||||
let local_has_oob = global_offset < off
|
||||
|| global_offset > off + len
|
||||
|| prev
|
||||
.get(global_offset as usize)
|
||||
.is_none_or(|&b| instr_from_byte(b).is_err());
|
||||
|| instr_from_byte(prev[global_offset as usize]).is_err()
|
||||
|| prev[global_offset as usize] == 0;
|
||||
has_oob |= local_has_oob;
|
||||
let label = labels.get(&global_offset).unwrap();
|
||||
if local_has_oob {
|
||||
|
@ -276,7 +242,11 @@ pub fn disasm<'a>(
|
|||
}
|
||||
|
||||
if has_oob {
|
||||
return Err(DisasmError::HasOutOfBoundsJumps);
|
||||
return Err(std::io::ErrorKind::InvalidInput.into());
|
||||
}
|
||||
|
||||
if has_cycle {
|
||||
return Err(std::io::ErrorKind::TimedOut.into());
|
||||
}
|
||||
|
||||
Ok(())
|
6
hbjit/Cargo.toml
Normal file
6
hbjit/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "hbjit"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
3
hbjit/src/main.rs
Normal file
3
hbjit/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
13
hblang/Cargo.toml
Normal file
13
hblang/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "hblang"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "hbc"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
hbbytecode = { version = "0.1.0", path = "../hbbytecode" }
|
||||
hbvm = { path = "../hbvm", features = ["nightly"] }
|
||||
regalloc2 = { git = "https://github.com/jakubDoka/regalloc2" }
|
945
hblang/README.md
Normal file
945
hblang/README.md
Normal file
|
@ -0,0 +1,945 @@
|
|||
# HERE SHALL THE DOCUMENTATION RESIDE
|
||||
|
||||
## Enforced Political Views
|
||||
|
||||
- worse is better
|
||||
- less is more
|
||||
- embrace `unsafe {}`
|
||||
- adhere `macro_rules!`
|
||||
- pessimization == death (put in `std::pin::Pin` and left with hungry crabs)
|
||||
- importing external dependencies == death (`fn(dependencies) -> ExecutionStrategy`)
|
||||
- above sell not be disputed, discussed, or questioned
|
||||
|
||||
## What hblang is
|
||||
|
||||
Holey-Bytes-Language (hblang for short) (*.hb) is the only true language targeting hbvm byte code. hblang is low level, manually managed, and procedural. Its rumored to be better then writing hbasm and you should probably use it for complex applications.
|
||||
|
||||
## What hblang isnt't
|
||||
|
||||
hblang knows what it isn't, because it knows what it is, hblang computes this by sub...
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Examples are also used in tests. To add an example that runs during testing add:
|
||||
<pre>
|
||||
#### <name>
|
||||
```hb
|
||||
<example>
|
||||
```
|
||||
</pre>
|
||||
and also:
|
||||
```rs
|
||||
<name> => README;
|
||||
```
|
||||
to the `run_tests` macro at the bottom of the `src/codegen.rs`.
|
||||
|
||||
### Tour Examples
|
||||
|
||||
Following examples incrementally introduce language features and syntax.
|
||||
|
||||
#### main_fn
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
#### arithmetic
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + (1 << 0) + -1
|
||||
}
|
||||
```
|
||||
|
||||
#### functions
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return add_one(10) + add_two(20)
|
||||
}
|
||||
|
||||
add_two := fn(x: int): int {
|
||||
return x + 2
|
||||
}
|
||||
|
||||
add_one := fn(x: int): int {
|
||||
return x + 1
|
||||
}
|
||||
```
|
||||
|
||||
#### comments
|
||||
```hb
|
||||
// commant is an item
|
||||
main := fn(): int {
|
||||
// comment is a statement
|
||||
|
||||
foo(/* comment is an exprression /* if you are crazy */ */)
|
||||
return 0
|
||||
}
|
||||
|
||||
foo := fn(comment: void): void return /* comment evaluates to void */
|
||||
|
||||
// comments might be formatted in the future
|
||||
```
|
||||
|
||||
#### if_statements
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return fib(10)
|
||||
}
|
||||
|
||||
fib := fn(x: int): int {
|
||||
if x <= 2 {
|
||||
return 1
|
||||
} else {
|
||||
return fib(x - 1) + fib(x - 2)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### variables
|
||||
```hb
|
||||
main := fn(): int {
|
||||
ඞ := 1
|
||||
b := 2
|
||||
ඞ += 1
|
||||
return ඞ - b
|
||||
}
|
||||
```
|
||||
|
||||
#### loops
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return fib(10)
|
||||
}
|
||||
|
||||
fib := fn(n: int): int {
|
||||
a := 0
|
||||
b := 1
|
||||
loop if n == 0 break else {
|
||||
c := a + b
|
||||
a = b
|
||||
b = c
|
||||
n -= 1
|
||||
}
|
||||
return a
|
||||
}
|
||||
```
|
||||
|
||||
#### pointers
|
||||
```hb
|
||||
main := fn(): int {
|
||||
a := 1
|
||||
b := &a
|
||||
modify(b)
|
||||
drop(a)
|
||||
stack_reclamation_edge_case := 0
|
||||
return *b - 2
|
||||
}
|
||||
|
||||
modify := fn(a: ^int): void {
|
||||
*a = 2
|
||||
return
|
||||
}
|
||||
|
||||
drop := fn(a: int): void {
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### structs
|
||||
```hb
|
||||
Ty := struct {
|
||||
// comment
|
||||
|
||||
a: int,
|
||||
b: int,
|
||||
}
|
||||
|
||||
Ty2 := struct {
|
||||
ty: Ty,
|
||||
c: int,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
finst := Ty2.{ty: Ty.{a: 4, b: 1}, c: 3}
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): int {
|
||||
.{a, b} := *t
|
||||
return a - b
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
return t
|
||||
}
|
||||
```
|
||||
|
||||
#### struct_operators
|
||||
```hb
|
||||
Point := struct {
|
||||
x: int,
|
||||
y: int,
|
||||
}
|
||||
|
||||
Rect := struct {
|
||||
a: Point,
|
||||
b: Point,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
a := Point.(1, 2)
|
||||
b := Point.(3, 4)
|
||||
|
||||
d := Rect.(a + b, b - a)
|
||||
d2 := Rect.(Point.(0, 0) - b, a)
|
||||
d2 += d
|
||||
|
||||
c := d2.a + d2.b
|
||||
return c.x + c.y
|
||||
}
|
||||
```
|
||||
|
||||
#### global_variables
|
||||
```hb
|
||||
global_var := 10
|
||||
|
||||
complex_global_var := fib(global_var) - 5
|
||||
|
||||
fib := fn(n: int): int {
|
||||
if 2 > n {
|
||||
return n
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
complex_global_var += 5
|
||||
return complex_global_var
|
||||
}
|
||||
```
|
||||
note: values of global variables are evaluated at compile time
|
||||
|
||||
#### directives
|
||||
```hb
|
||||
foo := @use("foo.hb")
|
||||
|
||||
main := fn(): int {
|
||||
byte := @as(u8, 10)
|
||||
same_type_as_byte := @as(@TypeOf(byte), 30)
|
||||
wide_uint := @as(u32, 40)
|
||||
truncated_uint := @as(u8, @intcast(wide_uint))
|
||||
size_of_Type_in_bytes := @sizeof(foo.Type)
|
||||
align_of_Type_in_bytes := @alignof(foo.Type)
|
||||
hardcoded_pointer := @as(^u8, @bitcast(10))
|
||||
ecall_that_returns_int := @as(int, @eca(1, foo.Type.(10, 20), 5, 6))
|
||||
return @inline(foo.foo)
|
||||
}
|
||||
|
||||
// in module: foo.hb
|
||||
|
||||
Type := struct {
|
||||
brah: int,
|
||||
blah: int,
|
||||
}
|
||||
|
||||
foo := fn(): int return 0
|
||||
```
|
||||
|
||||
- `@use(<string>)`: imports a module based of string, the string is passed to a loader that can be customized, default loader uses following syntax:
|
||||
- `((rel:|)(<path>)|git:<git-addr>:<path>)`: `rel:` and `''` prefixes both mean module is located at `path` relavive to the current file, `git:` takes a git url without `https://` passed as `git-addr`, `path` then refers to file within the repository
|
||||
- `@TypeOf(<expr>)`: results into literal type of whatever the type of `<expr>` is, `<expr>` is not included in final binary
|
||||
- `@as(<ty>, <expr>)`: hint to the compiler that `@TypeOf(<expr>) == <ty>`
|
||||
- `@intcast(<expr>)`: needs to be used when conversion of `@TypeOf(<expr>)` would loose precision (widening of integers is implicit)
|
||||
- `@sizeof(<ty>), @alignof(<ty>)`: I think explaining this would insult your intelligence
|
||||
- `@bitcast(<expr>)`: tell compiler to assume `@TypeOf(<expr>)` is whatever is inferred, so long as size and alignment did not change
|
||||
- `@eca(<ty>, ...<expr>)`: invoke `eca` instruction, where `<ty>` is the type this will return and `<expr>...` are arguments passed to the call
|
||||
- `@inline(<func>, ...<args>)`: equivalent to `<func>(...<args>)` but function is guaranteed to inline, compiler will otherwise never inline
|
||||
|
||||
#### c_strings
|
||||
```hb
|
||||
str_len := fn(str: ^u8): int {
|
||||
len := 0
|
||||
loop if *str == 0 break else {
|
||||
len += 1
|
||||
str += 1
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
// when string ends with '\0' its a C string and thus type is '^u8'
|
||||
some_str := "abඞ\n\r\t\{35}\{36373839}\0"
|
||||
len := str_len(some_str)
|
||||
some_other_str := "fff\0"
|
||||
lep := str_len(some_other_str)
|
||||
return lep + len
|
||||
}
|
||||
```
|
||||
|
||||
#### struct_patterns
|
||||
```hb
|
||||
.{fib, fib_iter, Fiber} := @use("fibs.hb")
|
||||
|
||||
main := fn(): int {
|
||||
.{a, b} := Fiber.{a: 10, b: 10}
|
||||
return fib(a) - fib_iter(b)
|
||||
}
|
||||
|
||||
// in module: fibs.hb
|
||||
|
||||
Fiber := struct {a: u8, b: u8}
|
||||
|
||||
fib := fn(n: int): int if n < 2 {
|
||||
return n
|
||||
} else {
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
fib_iter := fn(n: int): int {
|
||||
a := 0
|
||||
b := 1
|
||||
loop if n == 0 break else {
|
||||
c := a + b
|
||||
a = b
|
||||
b = c
|
||||
n -= 1
|
||||
}
|
||||
return a
|
||||
}
|
||||
```
|
||||
|
||||
#### arrays
|
||||
```hb
|
||||
main := fn(): int {
|
||||
addr := @as(u16, 0x1FF)
|
||||
msg := [u8].(0, 0, @as(u8, addr & 0xFF), @as(u8, addr >> 8 & 0xFF))
|
||||
_force_stack := &msg
|
||||
|
||||
arr := [int].(1, 2, 4)
|
||||
return pass(&arr) + msg[3]
|
||||
}
|
||||
|
||||
pass := fn(arr: ^[int; 3]): int {
|
||||
return arr[0] + arr[1] + arr[arr[1]]
|
||||
}
|
||||
```
|
||||
|
||||
#### inline
|
||||
```hb
|
||||
main := fn(): int {
|
||||
return @inline(foo, 1, 2, 3) - 6
|
||||
}
|
||||
|
||||
foo := fn(a: int, b: int, c: int): int {
|
||||
return a + b + c
|
||||
}
|
||||
```
|
||||
|
||||
#### idk
|
||||
```hb
|
||||
_edge_case := @as(int, idk)
|
||||
|
||||
main := fn(): int {
|
||||
big_array := @as([u8; 128], idk)
|
||||
i := 0
|
||||
loop if i >= 128 break else {
|
||||
big_array[i] = 69
|
||||
i += 1
|
||||
}
|
||||
return big_array[42]
|
||||
}
|
||||
```
|
||||
|
||||
#### wide_ret
|
||||
```hb
|
||||
OemIdent := struct {
|
||||
dos_version: [u8; 8],
|
||||
dos_version_name: [u8; 8],
|
||||
}
|
||||
|
||||
Stru := struct {
|
||||
a: u16,
|
||||
b: u16,
|
||||
}
|
||||
|
||||
small_struct := fn(): Stru {
|
||||
return .{a: 0, b: 0}
|
||||
}
|
||||
|
||||
main := fn(major: int, minor: int): OemIdent {
|
||||
small_struct()
|
||||
ver := [u8].(0, 0, 0, 0, 0, 0, 0, 0)
|
||||
return OemIdent.(ver, ver)
|
||||
}
|
||||
```
|
||||
|
||||
### Incomplete Examples
|
||||
|
||||
#### comptime_pointers
|
||||
```hb
|
||||
main := fn(): int {
|
||||
$integer := 7
|
||||
modify(&integer)
|
||||
return integer
|
||||
}
|
||||
|
||||
modify := fn($num: ^int): void {
|
||||
$: *num = 0
|
||||
}
|
||||
```
|
||||
|
||||
#### generic_types
|
||||
```hb
|
||||
MALLOC_SYS_CALL := 69
|
||||
FREE_SYS_CALL := 96
|
||||
|
||||
malloc := fn(size: uint, align: uint): ^void return @eca(MALLOC_SYS_CALL, size, align)
|
||||
free := fn(ptr: ^void, size: uint, align: uint): void return @eca(FREE_SYS_CALL, ptr, size, align)
|
||||
|
||||
Vec := fn($Elem: type): type {
|
||||
return struct {
|
||||
data: ^Elem,
|
||||
len: uint,
|
||||
cap: uint,
|
||||
}
|
||||
}
|
||||
|
||||
new := fn($Elem: type): Vec(Elem) return Vec(Elem).{data: @bitcast(0), len: 0, cap: 0}
|
||||
|
||||
deinit := fn($Elem: type, vec: ^Vec(Elem)): void {
|
||||
free(@bitcast(vec.data), vec.cap * @sizeof(Elem), @alignof(Elem));
|
||||
*vec = new(Elem)
|
||||
return
|
||||
}
|
||||
|
||||
push := fn($Elem: type, vec: ^Vec(Elem), value: Elem): ^Elem {
|
||||
if vec.len == vec.cap {
|
||||
if vec.cap == 0 {
|
||||
vec.cap = 1
|
||||
} else {
|
||||
vec.cap *= 2
|
||||
}
|
||||
|
||||
new_alloc := @as(^Elem, @bitcast(malloc(vec.cap * @sizeof(Elem), @alignof(Elem))))
|
||||
if new_alloc == 0 return 0
|
||||
|
||||
src_cursor := vec.data
|
||||
dst_cursor := new_alloc
|
||||
end := vec.data + vec.len
|
||||
|
||||
loop if src_cursor == end break else {
|
||||
*dst_cursor = *src_cursor
|
||||
src_cursor += 1
|
||||
dst_cursor += 1
|
||||
}
|
||||
|
||||
if vec.len != 0 {
|
||||
free(@bitcast(vec.data), vec.len * @sizeof(Elem), @alignof(Elem))
|
||||
}
|
||||
vec.data = new_alloc
|
||||
}
|
||||
|
||||
slot := vec.data + vec.len;
|
||||
*slot = value
|
||||
vec.len += 1
|
||||
return slot
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
vec := new(int)
|
||||
push(int, &vec, 69)
|
||||
res := *vec.data
|
||||
deinit(int, &vec)
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
#### generic_functions
|
||||
```hb
|
||||
add := fn($T: type, a: T, b: T): T return a + b
|
||||
|
||||
main := fn(): int {
|
||||
return add(u32, 2, 2) - add(int, 1, 3)
|
||||
}
|
||||
```
|
||||
|
||||
#### fb_driver
|
||||
```hb
|
||||
arm_fb_ptr := fn(): int return 100
|
||||
x86_fb_ptr := fn(): int return 100
|
||||
|
||||
check_platform := fn(): int {
|
||||
return x86_fb_ptr()
|
||||
}
|
||||
|
||||
set_pixel := fn(x: int, y: int, width: int): int {
|
||||
return y * width + x
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
fb_ptr := check_platform()
|
||||
width := 100
|
||||
height := 30
|
||||
x := 0
|
||||
y := 0
|
||||
//t := 0
|
||||
i := 0
|
||||
|
||||
loop {
|
||||
if x < height {
|
||||
//t += set_pixel(x, y, height)
|
||||
x += 1
|
||||
i += 1
|
||||
} else {
|
||||
x = 0
|
||||
y += 1
|
||||
if set_pixel(x, y, height) != i return 0
|
||||
if y == width break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
```
|
||||
|
||||
### Purely Testing Examples
|
||||
|
||||
#### comptime_min_reg_leak
|
||||
```hb
|
||||
a := @use("math.hb").min(100, 50)
|
||||
|
||||
main := fn(): int {
|
||||
return a
|
||||
}
|
||||
|
||||
// in module: math.hb
|
||||
|
||||
SIZEOF_INT := 32
|
||||
SHIFT := SIZEOF_INT - 1
|
||||
min := fn(a: int, b: int): int {
|
||||
c := a - b
|
||||
return b + (c & c >> SHIFT)
|
||||
}
|
||||
```
|
||||
|
||||
#### different_types
|
||||
```hb
|
||||
Color := struct {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
}
|
||||
|
||||
Point := struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
|
||||
Pixel := struct {
|
||||
color: Color,
|
||||
point: Point,
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
pixel := Pixel.{
|
||||
color: Color.{
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 255,
|
||||
},
|
||||
point: Point.{
|
||||
x: 0,
|
||||
y: 2,
|
||||
},
|
||||
}
|
||||
|
||||
soupan := 1
|
||||
if *(&pixel.point.x + soupan) != 2 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if *(&pixel.point.y - 1) != 0 {
|
||||
return 64
|
||||
}
|
||||
|
||||
return pixel.point.x + pixel.point.y + pixel.color.r
|
||||
+ pixel.color.g + pixel.color.b + pixel.color.a
|
||||
}
|
||||
```
|
||||
|
||||
#### struct_return_from_module_function
|
||||
```hb
|
||||
bar := @use("bar.hb")
|
||||
|
||||
main := fn(): int {
|
||||
return 7 - bar.foo().x - bar.foo().y - bar.foo().z
|
||||
}
|
||||
|
||||
// in module: bar.hb
|
||||
|
||||
|
||||
foo := fn(): Foo {
|
||||
return .{x: 3, y: 2, z: 2}
|
||||
}
|
||||
|
||||
Foo := struct {x: int, y: u32, z: u32}
|
||||
```
|
||||
|
||||
#### sort_something_viredly
|
||||
```hb
|
||||
main := fn(): int {
|
||||
foo := sqrt
|
||||
return 0
|
||||
}
|
||||
|
||||
sqrt := fn(x: int): int {
|
||||
temp := 0
|
||||
g := 0
|
||||
b := 32768
|
||||
bshift := 15
|
||||
loop if b == 0 break else {
|
||||
bshift -= 1
|
||||
temp = b + (g << 1)
|
||||
temp <<= bshift
|
||||
if x >= temp {
|
||||
g += b
|
||||
x -= temp
|
||||
}
|
||||
b >>= 1
|
||||
}
|
||||
return g
|
||||
}
|
||||
```
|
||||
|
||||
#### hex_octal_binary_literals
|
||||
```hb
|
||||
main := fn(): int {
|
||||
hex := 0xFF
|
||||
decimal := 255
|
||||
octal := 0o377
|
||||
binary := 0b11111111
|
||||
|
||||
if hex == decimal & octal == decimal & binary == decimal {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
```
|
||||
|
||||
#### structs_in_registers
|
||||
```hb
|
||||
ColorBGRA := struct {b: u8, g: u8, r: u8, a: u8}
|
||||
MAGENTA := ColorBGRA.{b: 205, g: 0, r: 205, a: 255}
|
||||
|
||||
main := fn(): int {
|
||||
color := MAGENTA
|
||||
return color.r
|
||||
}
|
||||
```
|
||||
|
||||
#### comptime_function_from_another_file
|
||||
```hb
|
||||
stn := @use("stn.hb")
|
||||
|
||||
CONST_A := 100
|
||||
CONST_B := 50
|
||||
a := stn.math.min(CONST_A, CONST_B)
|
||||
|
||||
main := fn(): int {
|
||||
return a
|
||||
}
|
||||
|
||||
// in module: stn.hb
|
||||
math := @use("math.hb")
|
||||
|
||||
// in module: math.hb
|
||||
SIZEOF_INT := 32
|
||||
SHIFT := SIZEOF_INT - 1
|
||||
min := fn(a: int, b: int): int {
|
||||
c := a - b
|
||||
return b + (c & c >> SHIFT)
|
||||
}
|
||||
```
|
||||
|
||||
### Just Testing Optimizations
|
||||
|
||||
#### const_folding_with_arg
|
||||
```hb
|
||||
main := fn(arg: int): int {
|
||||
// reduces to 0
|
||||
return arg + 0 - arg * 1 + arg + 1 + arg + 2 + arg + 3 - arg * 3 - 6
|
||||
}
|
||||
```
|
||||
|
||||
#### branch_assignments
|
||||
```hb
|
||||
main := fn(arg: int): int {
|
||||
if arg == 1 {
|
||||
arg = 1
|
||||
} else if arg == 0 {
|
||||
arg = 2
|
||||
} else {
|
||||
arg = 3
|
||||
}
|
||||
return arg
|
||||
}
|
||||
```
|
||||
|
||||
#### inline_test
|
||||
```hb
|
||||
Point := struct {x: int, y: int}
|
||||
Buffer := struct {}
|
||||
Transform := Point
|
||||
ColorBGRA := Point
|
||||
|
||||
line := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA, thickness: int): void {
|
||||
if true {
|
||||
if p0.x > p1.x {
|
||||
@inline(line_low, buffer, p1, p0, color)
|
||||
} else {
|
||||
@inline(line_low, buffer, p0, p1, color)
|
||||
}
|
||||
} else {
|
||||
if p0.y > p1.y {
|
||||
// blah, test leading new line on directives
|
||||
|
||||
@inline(line_high, buffer, p1, p0, color)
|
||||
} else {
|
||||
@inline(line_high, buffer, p0, p1, color)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
line_low := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA): void {
|
||||
return
|
||||
}
|
||||
|
||||
line_high := fn(buffer: Buffer, p0: Point, p1: Point, color: ColorBGRA): void {
|
||||
return
|
||||
}
|
||||
|
||||
screenidx := @use("screen.hb").screenidx
|
||||
|
||||
rect_line := fn(buffer: Buffer, pos: Point, tr: Transform, color: ColorBGRA, thickness: int): void {
|
||||
t := 0
|
||||
y := 0
|
||||
x := 0
|
||||
loop if t == thickness break else {
|
||||
y = pos.y
|
||||
x = pos.x
|
||||
loop if y == pos.y + tr.x break else {
|
||||
a := 1 + @inline(screenidx, 10)
|
||||
a = 1 + @inline(screenidx, 2)
|
||||
y += 1
|
||||
}
|
||||
t += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
random := @use("random.hb")
|
||||
|
||||
example := fn(): void {
|
||||
loop {
|
||||
random_x := @inline(random.integer, 0, 1024)
|
||||
random_y := random.integer(0, 768)
|
||||
a := @inline(screenidx, random_x)
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
main := fn(): int {
|
||||
line(.(), .(0, 0), .(0, 0), .(0, 0), 10)
|
||||
rect_line(.(), .(0, 0), .(0, 0), .(0, 0), 10)
|
||||
example()
|
||||
return 0
|
||||
}
|
||||
|
||||
// in module: screen.hb
|
||||
|
||||
screenidx := fn(orange: int): int {
|
||||
return orange
|
||||
}
|
||||
|
||||
// in module: random.hb
|
||||
|
||||
integer := fn(min: int, max: int): int {
|
||||
rng := @as(int, @eca(3, 4))
|
||||
|
||||
if min != 0 | max != 0 {
|
||||
return rng % (max - min + 1) + min
|
||||
}
|
||||
return rng
|
||||
}
|
||||
```
|
||||
|
||||
#### some_generic_code
|
||||
```hb
|
||||
some_func := fn($Elem: type): void {
|
||||
return
|
||||
}
|
||||
|
||||
main := fn(): void {
|
||||
some_func(u8)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### integer_inference_issues
|
||||
```hb
|
||||
.{integer_range} := @use("random.hb")
|
||||
main := fn(): void {
|
||||
a := integer_range(0, 1000)
|
||||
return
|
||||
}
|
||||
|
||||
// in module: random.hb
|
||||
integer_range := fn(min: uint, max: int): uint {
|
||||
return @eca(3, 4) % (@bitcast(max) - min + 1) + min
|
||||
}
|
||||
```
|
||||
|
||||
#### exhaustive_loop_testing
|
||||
```hb
|
||||
main := fn(): int {
|
||||
if multiple_breaks(0) != 3 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if multiple_breaks(4) != 10 {
|
||||
return 2
|
||||
}
|
||||
|
||||
if state_change_in_break(0) != 0 {
|
||||
return 3
|
||||
}
|
||||
|
||||
if state_change_in_break(4) != 10 {
|
||||
return 4
|
||||
}
|
||||
|
||||
if continue_and_state_change(10) != 10 {
|
||||
return 5
|
||||
}
|
||||
|
||||
if continue_and_state_change(3) != 0 {
|
||||
return 6
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
multiple_breaks := fn(arg: int): int {
|
||||
loop if arg < 10 {
|
||||
arg += 1
|
||||
if arg == 3 break
|
||||
} else break
|
||||
return arg
|
||||
}
|
||||
|
||||
state_change_in_break := fn(arg: int): int {
|
||||
loop if arg < 10 {
|
||||
if arg == 3 {
|
||||
arg = 0
|
||||
break
|
||||
}
|
||||
arg += 1
|
||||
} else break
|
||||
return arg
|
||||
}
|
||||
|
||||
continue_and_state_change := fn(arg: int): int {
|
||||
loop if arg < 10 {
|
||||
if arg == 2 {
|
||||
arg = 4
|
||||
continue
|
||||
}
|
||||
if arg == 3 {
|
||||
arg = 0
|
||||
break
|
||||
}
|
||||
arg += 1
|
||||
} else break
|
||||
return arg
|
||||
}
|
||||
```
|
||||
|
||||
#### writing_into_string
|
||||
```hb
|
||||
outl := fn(): void {
|
||||
msg := "whahaha\0"
|
||||
@as(u8, 0)
|
||||
return
|
||||
}
|
||||
|
||||
inl := fn(): void {
|
||||
msg := "luhahah\0"
|
||||
return
|
||||
}
|
||||
|
||||
main := fn(): void {
|
||||
outl()
|
||||
inl()
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### request_page
|
||||
```hb
|
||||
request_page := fn(page_count: u8): ^u8 {
|
||||
msg := "\{00}\{01}xxxxxxxx\0"
|
||||
msg_page_count := msg + 1;
|
||||
*msg_page_count = page_count
|
||||
return @eca(3, 2, msg, 12)
|
||||
}
|
||||
|
||||
create_back_buffer := fn(total_pages: int): ^u32 {
|
||||
if total_pages <= 0xFF {
|
||||
return @bitcast(request_page(total_pages))
|
||||
}
|
||||
ptr := request_page(255)
|
||||
remaining := total_pages - 0xFF
|
||||
loop if remaining <= 0 break else {
|
||||
if remaining < 0xFF {
|
||||
request_page(remaining)
|
||||
} else {
|
||||
request_page(0xFF)
|
||||
}
|
||||
remaining -= 0xFF
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
|
||||
main := fn(): void {
|
||||
create_back_buffer(400)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### tests_ptr_to_ptr_copy
|
||||
```hb
|
||||
main := fn(): int {
|
||||
back_buffer := @as([u8; 1024 * 10], idk)
|
||||
|
||||
n := 0
|
||||
loop if n >= 1024 break else {
|
||||
back_buffer[n] = 64
|
||||
n += 1
|
||||
}
|
||||
n = 1
|
||||
loop if n >= 10 break else {
|
||||
*(@as(^[u8; 1024], @bitcast(&back_buffer)) + n) = *@as(^[u8; 1024], @bitcast(&back_buffer))
|
||||
n += 1
|
||||
}
|
||||
return back_buffer[1024 * 2]
|
||||
}
|
||||
```
|
4
hblang/command-help.txt
Normal file
4
hblang/command-help.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
--fmt - format all source files
|
||||
--fmt-current - format mentioned file
|
||||
--fmt-stdout - dont write the formatted file but print it
|
||||
--threads <1...> - number of threads compiler can use [default: 1]
|
2786
hblang/src/codegen.rs
Normal file
2786
hblang/src/codegen.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,5 @@
|
|||
use crate::{instrs, EncodedInstr};
|
||||
|
||||
const fn ascii_mask(chars: &[u8]) -> u128 {
|
||||
let mut eq = 0;
|
||||
let mut i = 0;
|
||||
|
@ -17,7 +19,7 @@ pub struct Token {
|
|||
}
|
||||
|
||||
impl Token {
|
||||
pub fn range(&self) -> core::ops::Range<usize> {
|
||||
pub fn range(&self) -> std::ops::Range<usize> {
|
||||
self.start as usize..self.end as usize
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +34,6 @@ macro_rules! gen_token_kind {
|
|||
#[keywords] $(
|
||||
$keyword:ident = $keyword_lit:literal,
|
||||
)*
|
||||
#[const_keywords] $(
|
||||
$const_keyword:ident = $const_keyword_lit:literal,
|
||||
)*
|
||||
#[punkt] $(
|
||||
$punkt:ident = $punkt_lit:literal,
|
||||
)*
|
||||
|
@ -45,25 +44,22 @@ macro_rules! gen_token_kind {
|
|||
)*
|
||||
}
|
||||
) => {
|
||||
impl core::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
impl std::fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub const OPS: &[Self] = &[$($(Self::$op),*),*];
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
let sf = unsafe { &*(self as *const _ as *const u8) } ;
|
||||
match *self {
|
||||
$( Self::$pattern => concat!('<', stringify!($pattern), '>'), )*
|
||||
$( Self::$keyword => stringify!($keyword_lit), )*
|
||||
$( Self::$const_keyword => concat!('$', $const_keyword_lit), )*
|
||||
$( Self::$punkt => stringify!($punkt_lit), )*
|
||||
$($( Self::$op => $op_lit,
|
||||
$(Self::$assign => concat!($op_lit, "="),)?)*)*
|
||||
_ => unsafe { core::str::from_utf8_unchecked(core::slice::from_ref(&sf)) },
|
||||
_ => unsafe { std::str::from_utf8_unchecked(std::slice::from_ref(&sf)) },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,28 +72,18 @@ macro_rules! gen_token_kind {
|
|||
} + 1)
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[inline(always)]
|
||||
fn from_ident(ident: &[u8]) -> Self {
|
||||
$(const $keyword: &[u8] = $keyword_lit.as_bytes();)*
|
||||
match ident {
|
||||
$($keyword => Self::$keyword,)*
|
||||
$($keyword_lit => Self::$keyword,)*
|
||||
_ => Self::Ident,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
fn from_ct_ident(ident: &[u8]) -> Self {
|
||||
$(const $const_keyword: &[u8] = $const_keyword_lit.as_bytes();)*
|
||||
match ident {
|
||||
$($const_keyword => Self::$const_keyword,)*
|
||||
_ => Self::CtIdent,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
pub enum TokenKind {
|
||||
Not = b'!',
|
||||
|
@ -131,16 +117,24 @@ pub enum TokenKind {
|
|||
|
||||
Ident,
|
||||
Number,
|
||||
Float,
|
||||
Eof,
|
||||
|
||||
Ct,
|
||||
|
||||
Return,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
True,
|
||||
False,
|
||||
Idk,
|
||||
|
||||
Ctor,
|
||||
Tupl,
|
||||
Arr,
|
||||
TArrow,
|
||||
Range,
|
||||
|
||||
Or,
|
||||
And,
|
||||
|
@ -150,31 +144,7 @@ pub enum TokenKind {
|
|||
BSlash = b'\\',
|
||||
RBrack = b']',
|
||||
Xor = b'^',
|
||||
Under = b'_',
|
||||
Tick = b'`',
|
||||
|
||||
Slf,
|
||||
Return,
|
||||
If,
|
||||
Match,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
Packed,
|
||||
Enum,
|
||||
Union,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
Defer,
|
||||
|
||||
CtLoop,
|
||||
|
||||
// Unused = a-z
|
||||
LBrace = b'{',
|
||||
Bor = b'|',
|
||||
|
@ -199,19 +169,93 @@ pub enum TokenKind {
|
|||
ShlAss = b'<' - 5 + 128,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for TokenKind {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
core::fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenKind {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn cond_op(self, signed: bool) -> Option<(fn(u8, u8, i16) -> EncodedInstr, bool)> {
|
||||
Some((
|
||||
match self {
|
||||
Self::Le if signed => instrs::jgts,
|
||||
Self::Le => instrs::jgtu,
|
||||
Self::Lt if signed => instrs::jlts,
|
||||
Self::Lt => instrs::jltu,
|
||||
Self::Ge if signed => instrs::jlts,
|
||||
Self::Ge => instrs::jltu,
|
||||
Self::Gt if signed => instrs::jgts,
|
||||
Self::Gt => instrs::jgtu,
|
||||
Self::Eq => instrs::jne,
|
||||
Self::Ne => instrs::jeq,
|
||||
_ => return None,
|
||||
},
|
||||
matches!(self, Self::Lt | TokenKind::Gt),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn binop(self, signed: bool, size: u32) -> Option<fn(u8, u8, u8) -> EncodedInstr> {
|
||||
use instrs::*;
|
||||
|
||||
macro_rules! div { ($($op:ident),*) => {[$(|a, b, c| $op(a, 0, b, c)),*]}; }
|
||||
macro_rules! rem { ($($op:ident),*) => {[$(|a, b, c| $op(0, a, b, c)),*]}; }
|
||||
|
||||
let ops = match self {
|
||||
Self::Add => [add8, add16, add32, add64],
|
||||
Self::Sub => [sub8, sub16, sub32, sub64],
|
||||
Self::Mul => [mul8, mul16, mul32, mul64],
|
||||
Self::Div if signed => div!(dirs8, dirs16, dirs32, dirs64),
|
||||
Self::Div => div!(diru8, diru16, diru32, diru64),
|
||||
Self::Mod if signed => rem!(dirs8, dirs16, dirs32, dirs64),
|
||||
Self::Mod => rem!(diru8, diru16, diru32, diru64),
|
||||
Self::Band => return Some(and),
|
||||
Self::Bor => return Some(or),
|
||||
Self::Xor => return Some(xor),
|
||||
Self::Shl => [slu8, slu16, slu32, slu64],
|
||||
Self::Shr if signed => [srs8, srs16, srs32, srs64],
|
||||
Self::Shr => [sru8, sru16, sru32, sru64],
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(ops[size.ilog2() as usize])
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn imm_binop(self, signed: bool, size: u32) -> Option<fn(u8, u8, u64) -> EncodedInstr> {
|
||||
use instrs::*;
|
||||
macro_rules! def_op {
|
||||
($name:ident |$a:ident, $b:ident, $c:ident| $($tt:tt)*) => {
|
||||
macro_rules! $name {
|
||||
($$($$op:ident),*) => {
|
||||
[$$(
|
||||
|$a, $b, $c: u64| $$op($($tt)*),
|
||||
)*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
def_op!(basic_op | a, b, c | a, b, c as _);
|
||||
def_op!(sub_op | a, b, c | a, b, c.wrapping_neg() as _);
|
||||
|
||||
let ops = match self {
|
||||
Self::Add => basic_op!(addi8, addi16, addi32, addi64),
|
||||
Self::Sub => sub_op!(addi8, addi16, addi32, addi64),
|
||||
Self::Mul => basic_op!(muli8, muli16, muli32, muli64),
|
||||
Self::Band => return Some(andi),
|
||||
Self::Bor => return Some(ori),
|
||||
Self::Xor => return Some(xori),
|
||||
Self::Shr if signed => basic_op!(srui8, srui16, srui32, srui64),
|
||||
Self::Shr => basic_op!(srui8, srui16, srui32, srui64),
|
||||
Self::Shl => basic_op!(slui8, slui16, slui32, slui64),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(ops[size.ilog2() as usize])
|
||||
}
|
||||
|
||||
pub fn ass_op(self) -> Option<Self> {
|
||||
let id = (self as u8).saturating_sub(128);
|
||||
if ascii_mask(b"|+-*/%^&79") & (1u128 << id) == 0 {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { core::mem::transmute::<u8, Self>(id) })
|
||||
Some(unsafe { std::mem::transmute::<u8, Self>(id) })
|
||||
}
|
||||
|
||||
pub fn is_comutative(self) -> bool {
|
||||
|
@ -219,66 +263,15 @@ impl TokenKind {
|
|||
matches!(self, S::Eq | S::Ne | S::Bor | S::Xor | S::Band | S::Add | S::Mul)
|
||||
}
|
||||
|
||||
pub fn is_compatison(self) -> bool {
|
||||
matches!(self, Self::Lt | Self::Gt | Self::Ge | Self::Le | Self::Ne | Self::Eq)
|
||||
}
|
||||
|
||||
pub fn is_supported_float_op(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Add
|
||||
| Self::Sub
|
||||
| Self::Mul
|
||||
| Self::Div
|
||||
| Self::Eq
|
||||
| Self::Ne
|
||||
| Self::Le
|
||||
| Self::Ge
|
||||
| Self::Lt
|
||||
| Self::Gt
|
||||
)
|
||||
}
|
||||
|
||||
pub fn apply_binop(self, a: i64, b: i64, float: bool) -> i64 {
|
||||
if float {
|
||||
debug_assert!(self.is_supported_float_op());
|
||||
let [a, b] = [f64::from_bits(a as _), f64::from_bits(b as _)];
|
||||
let res = match self {
|
||||
Self::Add => a + b,
|
||||
Self::Sub => a - b,
|
||||
Self::Mul => a * b,
|
||||
Self::Div => a / b,
|
||||
Self::Eq => return (a == b) as i64,
|
||||
Self::Ne => return (a != b) as i64,
|
||||
Self::Lt => return (a < b) as i64,
|
||||
Self::Gt => return (a > b) as i64,
|
||||
Self::Le => return (a >= b) as i64,
|
||||
Self::Ge => return (a <= b) as i64,
|
||||
_ => todo!("floating point op: {self}"),
|
||||
};
|
||||
|
||||
return res.to_bits() as _;
|
||||
}
|
||||
|
||||
pub fn apply_binop(self, a: i64, b: i64) -> i64 {
|
||||
match self {
|
||||
Self::Add => a.wrapping_add(b),
|
||||
Self::Sub => a.wrapping_sub(b),
|
||||
Self::Mul => a.wrapping_mul(b),
|
||||
Self::Div if b == 0 => 0,
|
||||
Self::Div => a.wrapping_div(b),
|
||||
Self::Shl => a.wrapping_shl(b as _),
|
||||
Self::Eq => (a == b) as i64,
|
||||
Self::Ne => (a != b) as i64,
|
||||
Self::Lt => (a < b) as i64,
|
||||
Self::Gt => (a > b) as i64,
|
||||
Self::Le => (a >= b) as i64,
|
||||
Self::Ge => (a <= b) as i64,
|
||||
Self::Band => a & b,
|
||||
Self::Bor => a | b,
|
||||
Self::Xor => a ^ b,
|
||||
Self::Mod if b == 0 => 0,
|
||||
Self::Mod => a.wrapping_rem(b),
|
||||
Self::Shr => a.wrapping_shr(b as _),
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
}
|
||||
|
@ -289,29 +282,19 @@ impl TokenKind {
|
|||
&& self.precedence() != Self::Eof.precedence()
|
||||
}
|
||||
|
||||
pub fn apply_unop(&self, value: i64, float: bool) -> i64 {
|
||||
match self {
|
||||
Self::Sub if float => (-f64::from_bits(value as _)).to_bits() as _,
|
||||
Self::Sub => value.wrapping_neg(),
|
||||
Self::Not => (value == 0) as _,
|
||||
Self::Float if float => value,
|
||||
Self::Float => (value as f64).to_bits() as _,
|
||||
Self::Number if float => f64::from_bits(value as _) as _,
|
||||
Self::Number => value,
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn closing(&self) -> Option<TokenKind> {
|
||||
pub fn unop(&self) -> Option<fn(u8, u8) -> EncodedInstr> {
|
||||
Some(match self {
|
||||
Self::Ctor => Self::RBrace,
|
||||
Self::Tupl => Self::RParen,
|
||||
Self::LParen => Self::RParen,
|
||||
Self::LBrack => Self::RBrack,
|
||||
Self::LBrace => Self::RBrace,
|
||||
Self::Sub => instrs::neg,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn apply_unop(&self, value: i64) -> i64 {
|
||||
match self {
|
||||
Self::Sub => value.wrapping_neg(),
|
||||
s => todo!("{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen_token_kind! {
|
||||
|
@ -320,38 +303,23 @@ gen_token_kind! {
|
|||
CtIdent,
|
||||
Ident,
|
||||
Number,
|
||||
Float,
|
||||
Eof,
|
||||
Directive,
|
||||
#[keywords]
|
||||
Slf = "Self",
|
||||
Return = "return",
|
||||
If = "if",
|
||||
Match = "match",
|
||||
Else = "else",
|
||||
Loop = "loop",
|
||||
Break = "break",
|
||||
Continue = "continue",
|
||||
Fn = "fn",
|
||||
Struct = "struct",
|
||||
Packed = "packed",
|
||||
Enum = "enum",
|
||||
Union = "union",
|
||||
True = "true",
|
||||
False = "false",
|
||||
Null = "null",
|
||||
Idk = "idk",
|
||||
Die = "die",
|
||||
Defer = "defer",
|
||||
Under = "_",
|
||||
#[const_keywords]
|
||||
CtLoop = "loop",
|
||||
Return = b"return",
|
||||
If = b"if",
|
||||
Else = b"else",
|
||||
Loop = b"loop",
|
||||
Break = b"break",
|
||||
Continue = b"continue",
|
||||
Fn = b"fn",
|
||||
Struct = b"struct",
|
||||
True = b"true",
|
||||
False = b"false",
|
||||
Idk = b"idk",
|
||||
#[punkt]
|
||||
Ctor = ".{",
|
||||
Tupl = ".(",
|
||||
Arr = ".[",
|
||||
TArrow = "=>",
|
||||
Range = "..",
|
||||
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
|
||||
#[ops]
|
||||
#[prec]
|
||||
|
@ -391,7 +359,7 @@ gen_token_kind! {
|
|||
|
||||
pub struct Lexer<'a> {
|
||||
pos: u32,
|
||||
source: &'a [u8],
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
|
@ -399,59 +367,23 @@ impl<'a> Lexer<'a> {
|
|||
Self::restore(input, 0)
|
||||
}
|
||||
|
||||
pub fn uses(input: &'a str) -> impl Iterator<Item = &'a str> {
|
||||
let mut s = Self::new(input);
|
||||
core::iter::from_fn(move || loop {
|
||||
let t = s.eat();
|
||||
if t.kind == TokenKind::Eof {
|
||||
return None;
|
||||
}
|
||||
if t.kind == TokenKind::Directive
|
||||
&& s.slice(t.range()) == "use"
|
||||
&& s.eat().kind == TokenKind::LParen
|
||||
{
|
||||
let t = s.eat();
|
||||
if t.kind == TokenKind::DQuote {
|
||||
return Some(&s.slice(t.range())[1..t.range().len() - 1]);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn restore(input: &'a str, pos: u32) -> Self {
|
||||
Self { pos, source: input.as_bytes() }
|
||||
Self { pos, bytes: input.as_bytes() }
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &'a str {
|
||||
unsafe { core::str::from_utf8_unchecked(self.source) }
|
||||
unsafe { std::str::from_utf8_unchecked(self.bytes) }
|
||||
}
|
||||
|
||||
pub fn slice(&self, tok: core::ops::Range<usize>) -> &'a str {
|
||||
unsafe { core::str::from_utf8_unchecked(&self.source[tok]) }
|
||||
}
|
||||
|
||||
pub fn taste(&self) -> Token {
|
||||
Lexer { pos: self.pos, source: self.source }.eat()
|
||||
}
|
||||
|
||||
fn peek_n<const N: usize>(&self) -> Option<&[u8; N]> {
|
||||
if core::intrinsics::unlikely(self.pos as usize + N > self.source.len()) {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe {
|
||||
self.source
|
||||
.get_unchecked(self.pos as usize..self.pos as usize + N)
|
||||
.first_chunk()
|
||||
.unwrap_unchecked()
|
||||
})
|
||||
}
|
||||
pub fn slice(&self, tok: std::ops::Range<usize>) -> &'a str {
|
||||
unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) }
|
||||
}
|
||||
|
||||
fn peek(&self) -> Option<u8> {
|
||||
if core::intrinsics::unlikely(self.pos >= self.source.len() as u32) {
|
||||
if std::intrinsics::unlikely(self.pos >= self.bytes.len() as u32) {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { *self.source.get_unchecked(self.pos as usize) })
|
||||
Some(unsafe { *self.bytes.get_unchecked(self.pos as usize) })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,9 +394,9 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
|
||||
pub fn last(&mut self) -> Token {
|
||||
let mut token = self.eat();
|
||||
let mut token = self.next();
|
||||
loop {
|
||||
let next = self.eat();
|
||||
let next = self.next();
|
||||
if next.kind == TokenKind::Eof {
|
||||
break;
|
||||
}
|
||||
|
@ -473,7 +405,7 @@ impl<'a> Lexer<'a> {
|
|||
token
|
||||
}
|
||||
|
||||
pub fn eat(&mut self) -> Token {
|
||||
pub fn next(&mut self) -> Token {
|
||||
use TokenKind as T;
|
||||
loop {
|
||||
let mut start = self.pos;
|
||||
|
@ -488,7 +420,7 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let identity = |s: u8| unsafe { core::mem::transmute::<u8, T>(s) };
|
||||
let identity = |s: u8| unsafe { std::mem::transmute::<u8, T>(s) };
|
||||
|
||||
let kind = match c {
|
||||
..=b' ' => continue,
|
||||
|
@ -514,23 +446,11 @@ impl<'a> Lexer<'a> {
|
|||
while let Some(b'0'..=b'9') = self.peek() {
|
||||
self.advance();
|
||||
}
|
||||
|
||||
if self
|
||||
.peek_n()
|
||||
.map_or_else(|| self.peek() == Some(b'.'), |&[a, b]| a == b'.' && b != b'.')
|
||||
{
|
||||
self.pos += 1;
|
||||
while let Some(b'0'..=b'9') = self.peek() {
|
||||
self.advance();
|
||||
}
|
||||
T::Float
|
||||
} else {
|
||||
T::Number
|
||||
}
|
||||
T::Number
|
||||
}
|
||||
b'a'..=b'z' | b'A'..=b'Z' | b'_' | 127.. => {
|
||||
advance_ident(self);
|
||||
let ident = &self.source[start as usize..self.pos as usize];
|
||||
let ident = &self.bytes[start as usize..self.pos as usize];
|
||||
T::from_ident(ident)
|
||||
}
|
||||
b'"' | b'\'' => loop {
|
||||
|
@ -542,18 +462,10 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
},
|
||||
b'/' if self.advance_if(b'/') => {
|
||||
while let Some(l) = self.peek()
|
||||
while let Some(l) = self.advance()
|
||||
&& l != b'\n'
|
||||
{
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
let end = self.source[..self.pos as usize]
|
||||
.iter()
|
||||
.rposition(|&b| !b.is_ascii_whitespace())
|
||||
.map_or(self.pos, |i| i as u32 + 1);
|
||||
|
||||
return Token { kind: T::Comment, start, end };
|
||||
{}
|
||||
T::Comment
|
||||
}
|
||||
b'/' if self.advance_if(b'*') => {
|
||||
let mut depth = 1;
|
||||
|
@ -571,23 +483,14 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
b'.' if self.advance_if(b'{') => T::Ctor,
|
||||
b'.' if self.advance_if(b'(') => T::Tupl,
|
||||
b'.' if self.advance_if(b'[') => T::Arr,
|
||||
b'.' if self.advance_if(b'.') => T::Range,
|
||||
b'=' if self.advance_if(b'>') => T::TArrow,
|
||||
b'&' if self.advance_if(b'&') => T::And,
|
||||
b'|' if self.advance_if(b'|') => T::Or,
|
||||
b'$' if self.advance_if(b':') => T::Ct,
|
||||
b'@' => {
|
||||
b'@' | b'$' => {
|
||||
start += 1;
|
||||
advance_ident(self);
|
||||
identity(c)
|
||||
}
|
||||
b'$' => {
|
||||
start += 1;
|
||||
advance_ident(self);
|
||||
let ident = &self.source[start as usize..self.pos as usize];
|
||||
T::from_ct_ident(ident)
|
||||
}
|
||||
b'<' | b'>' if self.advance_if(c) => {
|
||||
identity(c - 5 + 128 * self.advance_if(b'=') as u8)
|
||||
}
|
1419
hblang/src/lib.rs
Normal file
1419
hblang/src/lib.rs
Normal file
File diff suppressed because it is too large
Load diff
28
hblang/src/main.rs
Normal file
28
hblang/src/main.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use std::num::NonZeroUsize;
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let args = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
|
||||
if args.contains(&"--help") || args.contains(&"-h") {
|
||||
println!("Usage: hbc [OPTIONS...] <FILE>");
|
||||
println!(include_str!("../command-help.txt"));
|
||||
return Err(std::io::ErrorKind::Other.into());
|
||||
}
|
||||
|
||||
hblang::run_compiler(
|
||||
args.get(1).copied().unwrap_or("main.hb"),
|
||||
hblang::Options {
|
||||
fmt: args.contains(&"--fmt"),
|
||||
fmt_current: args.contains(&"--fmt-current"),
|
||||
dump_asm: args.contains(&"--dump-asm"),
|
||||
extra_threads: args
|
||||
.iter()
|
||||
.position(|&a| a == "--threads")
|
||||
.map(|i| args[i + 1].parse::<NonZeroUsize>().expect("--threads expects integer"))
|
||||
.map_or(1, NonZeroUsize::get)
|
||||
- 1,
|
||||
},
|
||||
&mut std::io::stdout(),
|
||||
)
|
||||
}
|
1533
hblang/src/parser.rs
Normal file
1533
hblang/src/parser.rs
Normal file
File diff suppressed because it is too large
Load diff
2775
hblang/src/son.rs
Normal file
2775
hblang/src/son.rs
Normal file
File diff suppressed because it is too large
Load diff
28
hblang/tests/codegen_tests_arithmetic.txt
Normal file
28
hblang/tests/codegen_tests_arithmetic.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LI64 r32, 10d
|
||||
LI64 r33, 20d
|
||||
LI64 r34, 2d
|
||||
DIRS64 r33, r0, r33, r34
|
||||
SUB64 r32, r32, r33
|
||||
LI64 r33, 4d
|
||||
LI64 r34, 2d
|
||||
ADDI64 r34, r34, 2d
|
||||
MUL64 r33, r33, r34
|
||||
ADD64 r32, r32, r33
|
||||
LI64 r33, 4d
|
||||
MULI64 r33, r33, 4d
|
||||
SUB64 r32, r32, r33
|
||||
LI64 r33, 1d
|
||||
SLUI64 r33, r33, 0b
|
||||
ADD64 r32, r32, r33
|
||||
LI64 r33, 1d
|
||||
NEG r33, r33
|
||||
ADD64 r1, r32, r33
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 205
|
||||
ret: 0
|
||||
status: Ok(())
|
68
hblang/tests/codegen_tests_arrays.txt
Normal file
68
hblang/tests/codegen_tests_arrays.txt
Normal file
|
@ -0,0 +1,68 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -68d
|
||||
ST r31, r254, 28a, 40h
|
||||
LI64 r32, 511d
|
||||
LI64 r33, 0d
|
||||
ST r33, r254, 24a, 1h
|
||||
LI64 r33, 0d
|
||||
ST r33, r254, 25a, 1h
|
||||
CP r33, r32
|
||||
ANDI r33, r33, 255d
|
||||
ST r33, r254, 26a, 1h
|
||||
SRUI16 r32, r32, 8b
|
||||
ANDI r32, r32, 255d
|
||||
ST r32, r254, 27a, 1h
|
||||
CP r32, r0
|
||||
LD r32, r254, 24a, 4h
|
||||
ST r32, r254, 0a, 4h
|
||||
ADDI64 r32, r254, 0d
|
||||
LI64 r33, 1d
|
||||
ST r33, r254, 4a, 8h
|
||||
LI64 r33, 2d
|
||||
ST r33, r254, 12a, 8h
|
||||
LI64 r33, 4d
|
||||
ST r33, r254, 20a, 8h
|
||||
ADDI64 r33, r254, 4d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :pass
|
||||
CP r33, r1
|
||||
ADDI64 r34, r254, 0d
|
||||
LI64 r35, 3d
|
||||
ADD64 r34, r34, r35
|
||||
CP r35, r0
|
||||
LD r35, r34, 0a, 1h
|
||||
SXT8 r35, r35
|
||||
ADD64 r1, r33, r35
|
||||
LD r31, r254, 28a, 40h
|
||||
ADDI64 r254, r254, 68d
|
||||
JALA r0, r31, 0a
|
||||
pass:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
CP r33, r32
|
||||
LI64 r34, 0d
|
||||
MULI64 r34, r34, 8d
|
||||
ADD64 r33, r33, r34
|
||||
LD r34, r33, 0a, 8h
|
||||
CP r33, r32
|
||||
LI64 r35, 1d
|
||||
MULI64 r35, r35, 8d
|
||||
ADD64 r33, r33, r35
|
||||
LD r35, r33, 0a, 8h
|
||||
ADD64 r34, r34, r35
|
||||
CP r33, r32
|
||||
LI64 r35, 1d
|
||||
MULI64 r35, r35, 8d
|
||||
ADD64 r32, r32, r35
|
||||
LD r35, r32, 0a, 8h
|
||||
MULI64 r35, r35, 8d
|
||||
ADD64 r33, r33, r35
|
||||
LD r35, r33, 0a, 8h
|
||||
ADD64 r1, r34, r35
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 580
|
||||
ret: 8
|
||||
status: Ok(())
|
|
@ -9,24 +9,28 @@ main:
|
|||
CP r2, r33
|
||||
JAL r31, r0, :str_len
|
||||
CP r33, r1
|
||||
ADD64 r32, r33, r32
|
||||
CP r1, r32
|
||||
ADD64 r1, r33, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
str_len:
|
||||
CP r13, r2
|
||||
CP r15, r0
|
||||
CP r14, r15
|
||||
2: LD r16, r13, 0a, 1h
|
||||
ANDI r16, r16, 255d
|
||||
JNE r16, r15, :0
|
||||
CP r1, r14
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 0a, 48h
|
||||
CP r32, r2
|
||||
LI64 r33, 0d
|
||||
2: CP r34, r32
|
||||
CP r35, r0
|
||||
LD r35, r34, 0a, 1h
|
||||
LI64 r36, 0d
|
||||
JNE r35, r36, :0
|
||||
JMP :1
|
||||
0: ADDI64 r13, r13, 1d
|
||||
ADDI64 r14, r14, 1d
|
||||
0: ADDI64 r33, r33, 1d
|
||||
ADDI64 r32, r32, 1d
|
||||
JMP :2
|
||||
1: JALA r0, r31, 0a
|
||||
code size: 216
|
||||
1: CP r1, r33
|
||||
LD r31, r254, 0a, 48h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
code size: 270
|
||||
ret: 16
|
||||
status: Ok(())
|
17
hblang/tests/codegen_tests_comments.txt
Normal file
17
hblang/tests/codegen_tests_comments.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
foo:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :foo
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 143
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -0,0 +1,11 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LRA r32, r0, :a
|
||||
LD r1, r32, 0a, 8h
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 95
|
||||
ret: 50
|
||||
status: Ok(())
|
11
hblang/tests/codegen_tests_comptime_min_reg_leak.txt
Normal file
11
hblang/tests/codegen_tests_comptime_min_reg_leak.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LRA r32, r0, :a
|
||||
LD r1, r32, 0a, 8h
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 218
|
||||
ret: 50
|
||||
status: Ok(())
|
57
hblang/tests/codegen_tests_different_types.txt
Normal file
57
hblang/tests/codegen_tests_different_types.txt
Normal file
|
@ -0,0 +1,57 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -44d
|
||||
ST r31, r254, 12a, 32h
|
||||
LI64 r32, 255d
|
||||
ST r32, r254, 0a, 1h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 1a, 1h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 2a, 1h
|
||||
LI64 r32, 255d
|
||||
ST r32, r254, 3a, 1h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 4a, 4h
|
||||
LI64 r32, 2d
|
||||
ST r32, r254, 8a, 4h
|
||||
LI64 r32, 1d
|
||||
ADDI64 r33, r254, 4d
|
||||
CP r34, r32
|
||||
MULI64 r34, r34, 4d
|
||||
ADD64 r33, r33, r34
|
||||
CP r32, r0
|
||||
LD r32, r33, 0a, 4h
|
||||
LI64 r34, 2d
|
||||
JEQ r32, r34, :0
|
||||
LI64 r1, 0d
|
||||
JMP :1
|
||||
0: ADDI64 r34, r254, 8d
|
||||
ADDI64 r34, r34, -4d
|
||||
CP r32, r0
|
||||
LD r32, r34, 0a, 4h
|
||||
LI64 r33, 0d
|
||||
JEQ r32, r33, :2
|
||||
LI64 r1, 64d
|
||||
JMP :1
|
||||
2: CP r33, r0
|
||||
LD r33, r254, 4a, 4h
|
||||
CP r32, r0
|
||||
LD r32, r254, 8a, 4h
|
||||
ADD32 r33, r33, r32
|
||||
CP r32, r0
|
||||
LD r32, r254, 0a, 1h
|
||||
ADD32 r33, r33, r32
|
||||
CP r32, r0
|
||||
LD r32, r254, 1a, 1h
|
||||
ADD32 r33, r33, r32
|
||||
CP r32, r0
|
||||
LD r32, r254, 2a, 1h
|
||||
ADD32 r33, r33, r32
|
||||
CP r32, r0
|
||||
LD r32, r254, 3a, 1h
|
||||
ADD32 r1, r33, r32
|
||||
1: LD r31, r254, 12a, 32h
|
||||
ADDI64 r254, r254, 44d
|
||||
JALA r0, r31, 0a
|
||||
code size: 474
|
||||
ret: 512
|
||||
status: Ok(())
|
27
hblang/tests/codegen_tests_directives.txt
Normal file
27
hblang/tests/codegen_tests_directives.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -80d
|
||||
ST r31, r254, 16a, 64h
|
||||
LI64 r32, 10d
|
||||
LI64 r33, 30d
|
||||
LI64 r34, 40d
|
||||
LI64 r35, 16d
|
||||
LI64 r36, 8d
|
||||
LI64 r37, 10d
|
||||
LI64 r2, 1d
|
||||
LI64 r38, 10d
|
||||
ST r38, r254, 0a, 8h
|
||||
LI64 r38, 20d
|
||||
ST r38, r254, 8a, 8h
|
||||
LD r3, r254, 0a, 16h
|
||||
LI64 r5, 5d
|
||||
LI64 r6, 6d
|
||||
ECA
|
||||
CP r38, r1
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 16a, 64h
|
||||
ADDI64 r254, r254, 80d
|
||||
JALA r0, r31, 0a
|
||||
ev: Ecall
|
||||
code size: 230
|
||||
ret: 0
|
||||
status: Ok(())
|
74
hblang/tests/codegen_tests_fb_driver.txt
Normal file
74
hblang/tests/codegen_tests_fb_driver.txt
Normal file
|
@ -0,0 +1,74 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -80d
|
||||
ST r31, r254, 0a, 80h
|
||||
JAL r31, r0, :check_platform
|
||||
CP r32, r1
|
||||
LI64 r33, 100d
|
||||
LI64 r34, 30d
|
||||
LI64 r35, 0d
|
||||
LI64 r36, 0d
|
||||
4: CP r37, r35
|
||||
CP r38, r34
|
||||
ADDI64 r38, r38, 1d
|
||||
CMPS r37, r37, r38
|
||||
CMPUI r37, r37, 1d
|
||||
JEQ r37, r0, :0
|
||||
CP r2, r35
|
||||
CP r3, r36
|
||||
CP r4, r33
|
||||
JAL r31, r0, :set_pixel
|
||||
CP r37, r1
|
||||
CP r38, r35
|
||||
ADDI64 r38, r38, 1d
|
||||
CP r35, r38
|
||||
JMP :1
|
||||
0: CP r2, r35
|
||||
CP r3, r36
|
||||
CP r4, r33
|
||||
JAL r31, r0, :set_pixel
|
||||
CP r38, r1
|
||||
LI64 r35, 0d
|
||||
CP r39, r36
|
||||
ADDI64 r39, r39, 1d
|
||||
CP r36, r39
|
||||
1: CP r39, r36
|
||||
CP r40, r33
|
||||
CMPS r39, r39, r40
|
||||
CMPUI r39, r39, 0d
|
||||
NOT r39, r39
|
||||
JEQ r39, r0, :2
|
||||
JMP :3
|
||||
2: JMP :4
|
||||
3: LI64 r1, 0d
|
||||
LD r31, r254, 0a, 80h
|
||||
ADDI64 r254, r254, 80d
|
||||
JALA r0, r31, 0a
|
||||
set_pixel:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
CP r34, r4
|
||||
MUL64 r33, r33, r34
|
||||
ADD64 r33, r33, r32
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
check_platform:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :x86_fb_ptr
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
x86_fb_ptr:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r1, 100d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 511
|
||||
ret: 0
|
||||
status: Ok(())
|
32
hblang/tests/codegen_tests_functions.txt
Normal file
32
hblang/tests/codegen_tests_functions.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
add_one:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
CP r32, r2
|
||||
ADDI64 r1, r32, 1d
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
add_two:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
CP r32, r2
|
||||
ADDI64 r1, r32, 2d
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :add_one
|
||||
CP r32, r1
|
||||
LI64 r2, 20d
|
||||
JAL r31, r0, :add_two
|
||||
CP r33, r1
|
||||
ADD64 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 257
|
||||
ret: 33
|
||||
status: Ok(())
|
38
hblang/tests/codegen_tests_generic_functions.txt
Normal file
38
hblang/tests/codegen_tests_generic_functions.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
add:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
ADD64 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
add:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
ADD32 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LI64 r2, 2d
|
||||
LI64 r3, 2d
|
||||
JAL r31, r0, :add
|
||||
CP r32, r1
|
||||
LI64 r2, 1d
|
||||
LI64 r3, 3d
|
||||
JAL r31, r0, :add
|
||||
CP r33, r1
|
||||
CP r34, r32
|
||||
SXT32 r34, r34
|
||||
SUB64 r1, r34, r33
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 275
|
||||
ret: 0
|
||||
status: Ok(())
|
142
hblang/tests/codegen_tests_generic_types.txt
Normal file
142
hblang/tests/codegen_tests_generic_types.txt
Normal file
|
@ -0,0 +1,142 @@
|
|||
deinit:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
LD r2, r32, 0a, 8h
|
||||
LD r33, r32, 16a, 8h
|
||||
MULI64 r33, r33, 8d
|
||||
CP r3, r33
|
||||
LI64 r4, 8d
|
||||
JAL r31, r0, :free
|
||||
CP r1, r32
|
||||
JAL r31, r0, :new
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
free:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
CP r34, r4
|
||||
LRA r35, r0, :FREE_SYS_CALL
|
||||
LD r2, r35, 0a, 8h
|
||||
CP r3, r32
|
||||
CP r4, r33
|
||||
CP r5, r34
|
||||
ECA
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 24a, 24h
|
||||
ADDI64 r1, r254, 0d
|
||||
JAL r31, r0, :new
|
||||
ADDI64 r32, r254, 0d
|
||||
CP r2, r32
|
||||
LI64 r3, 69d
|
||||
JAL r31, r0, :push
|
||||
CP r32, r1
|
||||
LD r32, r254, 0a, 8h
|
||||
LD r33, r32, 0a, 8h
|
||||
ADDI64 r32, r254, 0d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :deinit
|
||||
CP r1, r33
|
||||
LD r31, r254, 24a, 24h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
malloc:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
LRA r34, r0, :MALLOC_SYS_CALL
|
||||
LD r2, r34, 0a, 8h
|
||||
CP r3, r32
|
||||
CP r4, r33
|
||||
ECA
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
new:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r1
|
||||
LI64 r33, 0d
|
||||
ST r33, r32, 0a, 8h
|
||||
LI64 r33, 0d
|
||||
ST r33, r32, 8a, 8h
|
||||
LI64 r33, 0d
|
||||
ST r33, r32, 16a, 8h
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
push:
|
||||
ADDI64 r254, r254, -72d
|
||||
ST r31, r254, 0a, 72h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
LD r34, r32, 8a, 8h
|
||||
LD r35, r32, 16a, 8h
|
||||
JNE r34, r35, :0
|
||||
LD r35, r32, 16a, 8h
|
||||
LI64 r34, 0d
|
||||
JNE r35, r34, :1
|
||||
LI64 r34, 1d
|
||||
ST r34, r32, 16a, 8h
|
||||
JMP :2
|
||||
1: LD r34, r32, 16a, 8h
|
||||
MULI64 r34, r34, 2d
|
||||
ST r34, r32, 16a, 8h
|
||||
2: LD r34, r32, 16a, 8h
|
||||
MULI64 r34, r34, 8d
|
||||
CP r2, r34
|
||||
LI64 r3, 8d
|
||||
JAL r31, r0, :malloc
|
||||
CP r34, r1
|
||||
LI64 r35, 0d
|
||||
JNE r34, r35, :3
|
||||
LI64 r1, 0d
|
||||
JMP :4
|
||||
3: LD r35, r32, 0a, 8h
|
||||
CP r36, r34
|
||||
LD r37, r32, 0a, 8h
|
||||
LD r38, r32, 8a, 8h
|
||||
MULI64 r38, r38, 8d
|
||||
ADD64 r37, r37, r38
|
||||
7: JNE r35, r37, :5
|
||||
JMP :6
|
||||
5: CP r38, r36
|
||||
CP r39, r35
|
||||
BMC r39, r38, 8h
|
||||
ADDI64 r35, r35, 8d
|
||||
ADDI64 r36, r36, 8d
|
||||
JMP :7
|
||||
6: LD r38, r32, 8a, 8h
|
||||
LI64 r39, 0d
|
||||
JEQ r38, r39, :8
|
||||
LD r2, r32, 0a, 8h
|
||||
LD r39, r32, 8a, 8h
|
||||
MULI64 r39, r39, 8d
|
||||
CP r3, r39
|
||||
LI64 r4, 8d
|
||||
JAL r31, r0, :free
|
||||
8: ST r34, r32, 0a, 8h
|
||||
0: LD r34, r32, 0a, 8h
|
||||
LD r39, r32, 8a, 8h
|
||||
MULI64 r39, r39, 8d
|
||||
ADD64 r34, r34, r39
|
||||
CP r39, r34
|
||||
ST r33, r39, 0a, 8h
|
||||
LD r39, r32, 8a, 8h
|
||||
ADDI64 r39, r39, 1d
|
||||
ST r39, r32, 8a, 8h
|
||||
CP r1, r34
|
||||
4: LD r31, r254, 0a, 72h
|
||||
ADDI64 r254, r254, 72d
|
||||
JALA r0, r31, 0a
|
||||
code size: 1201
|
||||
ret: 69
|
||||
status: Ok(())
|
16
hblang/tests/codegen_tests_global_variables.txt
Normal file
16
hblang/tests/codegen_tests_global_variables.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
LRA r32, r0, :complex_global_var
|
||||
LRA r33, r0, :complex_global_var
|
||||
LD r34, r33, 0a, 8h
|
||||
ADDI64 r34, r34, 5d
|
||||
ST r34, r32, 0a, 8h
|
||||
LRA r32, r0, :complex_global_var
|
||||
LD r1, r32, 0a, 8h
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 146
|
||||
ret: 55
|
||||
status: Ok(())
|
28
hblang/tests/codegen_tests_hex_octal_binary_literals.txt
Normal file
28
hblang/tests/codegen_tests_hex_octal_binary_literals.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
LI64 r32, 255d
|
||||
LI64 r33, 255d
|
||||
LI64 r34, 255d
|
||||
LI64 r35, 255d
|
||||
CMPS r32, r32, r33
|
||||
CMPUI r32, r32, 0d
|
||||
NOT r32, r32
|
||||
CMPS r34, r34, r33
|
||||
CMPUI r34, r34, 0d
|
||||
NOT r34, r34
|
||||
AND r32, r32, r34
|
||||
CMPS r35, r35, r33
|
||||
CMPUI r35, r35, 0d
|
||||
NOT r35, r35
|
||||
AND r32, r32, r35
|
||||
JEQ r32, r0, :0
|
||||
LI64 r1, 0d
|
||||
JMP :1
|
||||
0: LI64 r1, 1d
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 199
|
||||
ret: 0
|
||||
status: Ok(())
|
25
hblang/tests/codegen_tests_idk.txt
Normal file
25
hblang/tests/codegen_tests_idk.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -160d
|
||||
ST r31, r254, 128a, 32h
|
||||
LI64 r32, 0d
|
||||
2: LI64 r33, 128d
|
||||
JLTS r32, r33, :0
|
||||
JMP :1
|
||||
0: ADDI64 r33, r254, 0d
|
||||
CP r34, r32
|
||||
ADD64 r33, r33, r34
|
||||
LI64 r34, 69d
|
||||
ST r34, r33, 0a, 1h
|
||||
ADDI64 r32, r32, 1d
|
||||
JMP :2
|
||||
1: ADDI64 r33, r254, 0d
|
||||
LI64 r34, 42d
|
||||
ADD64 r33, r33, r34
|
||||
CP r1, r0
|
||||
LD r1, r33, 0a, 1h
|
||||
LD r31, r254, 128a, 32h
|
||||
ADDI64 r254, r254, 160d
|
||||
JALA r0, r31, 0a
|
||||
code size: 195
|
||||
ret: 69
|
||||
status: Ok(())
|
32
hblang/tests/codegen_tests_if_statements.txt
Normal file
32
hblang/tests/codegen_tests_if_statements.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
fib:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
LI64 r33, 2d
|
||||
JGTS r32, r33, :0
|
||||
LI64 r1, 1d
|
||||
JMP :1
|
||||
0: CP r33, r32
|
||||
ADDI64 r33, r33, -1d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :fib
|
||||
CP r33, r1
|
||||
ADDI64 r32, r32, -2d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :fib
|
||||
CP r32, r1
|
||||
ADD64 r1, r33, r32
|
||||
1: LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 231
|
||||
ret: 55
|
||||
status: Ok(())
|
13
hblang/tests/codegen_tests_inline.txt
Normal file
13
hblang/tests/codegen_tests_inline.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r33, 1d
|
||||
ADDI64 r33, r33, 2d
|
||||
ADDI64 r32, r33, 3d
|
||||
ADDI64 r1, r32, -6d
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 110
|
||||
ret: 0
|
||||
status: Ok(())
|
161
hblang/tests/codegen_tests_inline_test.txt
Normal file
161
hblang/tests/codegen_tests_inline_test.txt
Normal file
|
@ -0,0 +1,161 @@
|
|||
example:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 0a, 48h
|
||||
LI64 r2, 3d
|
||||
LI64 r3, 4d
|
||||
ECA
|
||||
CP r33, r1
|
||||
LI64 r34, 0d
|
||||
LI64 r35, 0d
|
||||
CMPS r34, r34, r35
|
||||
CMPUI r34, r34, 0d
|
||||
LI64 r35, 1024d
|
||||
LI64 r36, 0d
|
||||
CMPS r35, r35, r36
|
||||
CMPUI r35, r35, 0d
|
||||
OR r34, r34, r35
|
||||
JEQ r34, r0, :0
|
||||
CP r34, r33
|
||||
LI64 r35, 1024d
|
||||
ADDI64 r35, r35, 0d
|
||||
ADDI64 r35, r35, 1d
|
||||
DIRS64 r0, r34, r34, r35
|
||||
ADDI64 r32, r34, 0d
|
||||
JMP :1
|
||||
0: CP r32, r33
|
||||
1: LI64 r2, 0d
|
||||
LI64 r3, 768d
|
||||
JAL r31, r0, :integer
|
||||
CP r33, r1
|
||||
CP r34, r32
|
||||
JMP :2
|
||||
2: LD r31, r254, 0a, 48h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
integer:
|
||||
ADDI64 r254, r254, -56d
|
||||
ST r31, r254, 0a, 56h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
LI64 r2, 3d
|
||||
LI64 r3, 4d
|
||||
ECA
|
||||
CP r34, r1
|
||||
CP r35, r32
|
||||
LI64 r36, 0d
|
||||
CMPS r35, r35, r36
|
||||
CMPUI r35, r35, 0d
|
||||
CP r36, r33
|
||||
LI64 r37, 0d
|
||||
CMPS r36, r36, r37
|
||||
CMPUI r36, r36, 0d
|
||||
OR r35, r35, r36
|
||||
JEQ r35, r0, :0
|
||||
CP r35, r34
|
||||
SUB64 r33, r33, r32
|
||||
ADDI64 r33, r33, 1d
|
||||
DIRS64 r0, r35, r35, r33
|
||||
ADD64 r1, r35, r32
|
||||
JMP :1
|
||||
0: CP r1, r34
|
||||
1: LD r31, r254, 0a, 56h
|
||||
ADDI64 r254, r254, 56d
|
||||
JALA r0, r31, 0a
|
||||
line:
|
||||
ADDI64 r254, r254, -80d
|
||||
ST r31, r254, 48a, 32h
|
||||
ST r2, r254, 16a, 16h
|
||||
ST r4, r254, 0a, 16h
|
||||
ST r6, r254, 32a, 16h
|
||||
CP r32, r8
|
||||
LI64 r33, 1d
|
||||
JEQ r33, r0, :0
|
||||
LD r33, r254, 16a, 8h
|
||||
LD r34, r254, 0a, 8h
|
||||
JGTS r33, r34, :1
|
||||
JMP :1
|
||||
1: JMP :2
|
||||
0: LD r34, r254, 24a, 8h
|
||||
LD r33, r254, 8a, 8h
|
||||
JGTS r34, r33, :2
|
||||
JMP :2
|
||||
2: LD r31, r254, 48a, 32h
|
||||
ADDI64 r254, r254, 80d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -64d
|
||||
ST r31, r254, 48a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 8a, 8h
|
||||
LD r2, r254, 0a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 16a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 24a, 8h
|
||||
LD r4, r254, 16a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 32a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 40a, 8h
|
||||
LD r6, r254, 32a, 16h
|
||||
LI64 r8, 10d
|
||||
JAL r31, r0, :line
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 8a, 8h
|
||||
LD r2, r254, 0a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 16a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 24a, 8h
|
||||
LD r4, r254, 16a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 32a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 40a, 8h
|
||||
LD r6, r254, 32a, 16h
|
||||
LI64 r8, 10d
|
||||
JAL r31, r0, :rect_line
|
||||
JAL r31, r0, :example
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 48a, 16h
|
||||
ADDI64 r254, r254, 64d
|
||||
JALA r0, r31, 0a
|
||||
rect_line:
|
||||
ADDI64 r254, r254, -112d
|
||||
ST r31, r254, 48a, 64h
|
||||
ST r2, r254, 0a, 16h
|
||||
ST r4, r254, 16a, 16h
|
||||
ST r6, r254, 32a, 16h
|
||||
CP r32, r8
|
||||
LI64 r33, 0d
|
||||
LI64 r34, 0d
|
||||
LI64 r35, 0d
|
||||
5: JNE r33, r32, :0
|
||||
JMP :1
|
||||
0: LD r34, r254, 8a, 8h
|
||||
LD r35, r254, 0a, 8h
|
||||
4: LD r36, r254, 8a, 8h
|
||||
LD r37, r254, 16a, 8h
|
||||
ADD64 r36, r36, r37
|
||||
JNE r34, r36, :2
|
||||
JMP :3
|
||||
2: LI64 r36, 1d
|
||||
LI64 r37, 10d
|
||||
ADD64 r36, r36, r37
|
||||
LI64 r37, 1d
|
||||
LI64 r38, 2d
|
||||
ADD64 r36, r37, r38
|
||||
ADDI64 r34, r34, 1d
|
||||
JMP :4
|
||||
3: ADDI64 r33, r33, 1d
|
||||
JMP :5
|
||||
1: LD r31, r254, 48a, 64h
|
||||
ADDI64 r254, r254, 112d
|
||||
JALA r0, r31, 0a
|
||||
code size: 1400
|
||||
ret: 0
|
||||
status: Ok(())
|
29
hblang/tests/codegen_tests_integer_inference_issues.txt
Normal file
29
hblang/tests/codegen_tests_integer_inference_issues.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
integer_range:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
CP r33, r3
|
||||
LI64 r2, 3d
|
||||
LI64 r3, 4d
|
||||
ECA
|
||||
CP r34, r1
|
||||
SUB64 r33, r33, r32
|
||||
ADDI64 r33, r33, 1d
|
||||
DIRU64 r0, r34, r34, r33
|
||||
ADD64 r1, r34, r32
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r2, 0d
|
||||
LI64 r3, 1000d
|
||||
JAL r31, r0, :integer_range
|
||||
CP r32, r1
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 210
|
||||
ret: 42
|
||||
status: Ok(())
|
30
hblang/tests/codegen_tests_loops.txt
Normal file
30
hblang/tests/codegen_tests_loops.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
fib:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
LI64 r33, 0d
|
||||
LI64 r34, 1d
|
||||
2: LI64 r35, 0d
|
||||
JNE r32, r35, :0
|
||||
JMP :1
|
||||
0: CP r35, r33
|
||||
ADD64 r35, r35, r34
|
||||
CP r33, r34
|
||||
CP r34, r35
|
||||
ADDI64 r32, r32, -1d
|
||||
JMP :2
|
||||
1: CP r1, r33
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 218
|
||||
ret: 55
|
||||
status: Ok(())
|
35
hblang/tests/codegen_tests_pointers.txt
Normal file
35
hblang/tests/codegen_tests_pointers.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
drop:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
CP r32, r2
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 8a, 32h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 0a, 8h
|
||||
ADDI64 r32, r254, 0d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :modify
|
||||
LD r2, r254, 0a, 8h
|
||||
JAL r31, r0, :drop
|
||||
LI64 r33, 0d
|
||||
LD r34, r32, 0a, 8h
|
||||
ADDI64 r1, r34, -2d
|
||||
LD r31, r254, 8a, 32h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
modify:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r32, r2
|
||||
LI64 r33, 2d
|
||||
ST r33, r32, 0a, 8h
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 312
|
||||
ret: 0
|
||||
status: Ok(())
|
59
hblang/tests/codegen_tests_request_page.txt
Normal file
59
hblang/tests/codegen_tests_request_page.txt
Normal file
|
@ -0,0 +1,59 @@
|
|||
create_back_buffer:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
LI64 r33, 255d
|
||||
JGTS r32, r33, :0
|
||||
CP r2, r32
|
||||
JAL r31, r0, :request_page
|
||||
JMP :1
|
||||
0: LI64 r2, 255d
|
||||
JAL r31, r0, :request_page
|
||||
CP r33, r1
|
||||
ADDI64 r32, r32, -255d
|
||||
6: LI64 r34, 0d
|
||||
JGTS r32, r34, :2
|
||||
JMP :3
|
||||
2: LI64 r34, 255d
|
||||
JLTS r32, r34, :4
|
||||
LI64 r2, 255d
|
||||
JAL r31, r0, :request_page
|
||||
CP r34, r1
|
||||
JMP :5
|
||||
4: CP r2, r32
|
||||
JAL r31, r0, :request_page
|
||||
CP r34, r1
|
||||
5: ADDI64 r32, r32, -255d
|
||||
JMP :6
|
||||
3: CP r1, r33
|
||||
1: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r2, 400d
|
||||
JAL r31, r0, :create_back_buffer
|
||||
CP r32, r1
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
request_page:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
LRA r33, r0, :"\0\u{1}xxxxxxxx\0"
|
||||
CP r34, r33
|
||||
ADDI64 r34, r34, 1d
|
||||
ST r32, r34, 0a, 1h
|
||||
LI64 r2, 3d
|
||||
LI64 r3, 2d
|
||||
CP r4, r33
|
||||
LI64 r5, 12d
|
||||
ECA
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 443
|
||||
ret: 42
|
||||
status: Ok(())
|
16
hblang/tests/codegen_tests_some_generic_code.txt
Normal file
16
hblang/tests/codegen_tests_some_generic_code.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :some_func
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
some_func:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 133
|
||||
ret: 0
|
||||
status: Ok(())
|
11
hblang/tests/codegen_tests_sort_something_viredly.txt
Normal file
11
hblang/tests/codegen_tests_sort_something_viredly.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r32, 1610612737d
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 87
|
||||
ret: 0
|
||||
status: Ok(())
|
75
hblang/tests/codegen_tests_struct_operators.txt
Normal file
75
hblang/tests/codegen_tests_struct_operators.txt
Normal file
|
@ -0,0 +1,75 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -136d
|
||||
ST r31, r254, 112a, 24h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 32a, 8h
|
||||
LI64 r32, 2d
|
||||
ST r32, r254, 40a, 8h
|
||||
LI64 r32, 3d
|
||||
ST r32, r254, 16a, 8h
|
||||
LI64 r32, 4d
|
||||
ST r32, r254, 24a, 8h
|
||||
LD r32, r254, 32a, 8h
|
||||
LD r33, r254, 16a, 8h
|
||||
ADD64 r32, r32, r33
|
||||
ST r32, r254, 48a, 8h
|
||||
LD r32, r254, 40a, 8h
|
||||
LD r33, r254, 24a, 8h
|
||||
ADD64 r32, r32, r33
|
||||
ST r32, r254, 56a, 8h
|
||||
LD r32, r254, 16a, 8h
|
||||
LD r33, r254, 32a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
ST r32, r254, 64a, 8h
|
||||
LD r32, r254, 24a, 8h
|
||||
LD r33, r254, 40a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
ST r32, r254, 72a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 8a, 8h
|
||||
LD r32, r254, 0a, 8h
|
||||
LD r33, r254, 16a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
ST r32, r254, 80a, 8h
|
||||
LD r32, r254, 8a, 8h
|
||||
LD r33, r254, 24a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
ST r32, r254, 88a, 8h
|
||||
ADDI64 r32, r254, 32d
|
||||
ADDI64 r33, r254, 96d
|
||||
BMC r32, r33, 16h
|
||||
LD r33, r254, 80a, 8h
|
||||
LD r32, r254, 48a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 80a, 8h
|
||||
LD r33, r254, 88a, 8h
|
||||
LD r32, r254, 56a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 88a, 8h
|
||||
LD r33, r254, 96a, 8h
|
||||
LD r32, r254, 64a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 96a, 8h
|
||||
LD r33, r254, 104a, 8h
|
||||
LD r32, r254, 72a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 104a, 8h
|
||||
LD r33, r254, 80a, 8h
|
||||
LD r32, r254, 96a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 96a, 8h
|
||||
LD r33, r254, 88a, 8h
|
||||
LD r32, r254, 104a, 8h
|
||||
ADD64 r33, r33, r32
|
||||
ST r33, r254, 104a, 8h
|
||||
LD r33, r254, 96a, 8h
|
||||
LD r32, r254, 104a, 8h
|
||||
ADD64 r1, r33, r32
|
||||
LD r31, r254, 112a, 24h
|
||||
ADDI64 r254, r254, 136d
|
||||
JALA r0, r31, 0a
|
||||
code size: 778
|
||||
ret: 10
|
||||
status: Ok(())
|
65
hblang/tests/codegen_tests_struct_patterns.txt
Normal file
65
hblang/tests/codegen_tests_struct_patterns.txt
Normal file
|
@ -0,0 +1,65 @@
|
|||
fib:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
LI64 r33, 2d
|
||||
JLTS r32, r33, :0
|
||||
CP r33, r32
|
||||
ADDI64 r33, r33, -1d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :fib
|
||||
CP r33, r1
|
||||
CP r34, r32
|
||||
ADDI64 r34, r34, -2d
|
||||
CP r2, r34
|
||||
JAL r31, r0, :fib
|
||||
CP r34, r1
|
||||
ADD64 r1, r33, r34
|
||||
JMP :1
|
||||
0: CP r1, r32
|
||||
1: LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
fib_iter:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
LI64 r33, 0d
|
||||
LI64 r34, 1d
|
||||
2: LI64 r35, 0d
|
||||
JNE r32, r35, :0
|
||||
JMP :1
|
||||
0: CP r35, r33
|
||||
ADD64 r35, r35, r34
|
||||
CP r33, r34
|
||||
CP r34, r35
|
||||
ADDI64 r32, r32, -1d
|
||||
JMP :2
|
||||
1: CP r1, r33
|
||||
LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -26d
|
||||
ST r31, r254, 2a, 24h
|
||||
LI64 r32, 10d
|
||||
ST r32, r254, 0a, 1h
|
||||
LI64 r32, 10d
|
||||
ST r32, r254, 1a, 1h
|
||||
CP r32, r0
|
||||
LD r32, r254, 0a, 1h
|
||||
CP r33, r0
|
||||
LD r33, r254, 1a, 1h
|
||||
CP r2, r32
|
||||
JAL r31, r0, :fib
|
||||
CP r32, r1
|
||||
CP r2, r33
|
||||
JAL r31, r0, :fib_iter
|
||||
CP r33, r1
|
||||
SUB64 r1, r32, r33
|
||||
LD r31, r254, 2a, 24h
|
||||
ADDI64 r254, r254, 26d
|
||||
JALA r0, r31, 0a
|
||||
code size: 452
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -0,0 +1,39 @@
|
|||
foo:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 16a, 16h
|
||||
LI64 r32, 3d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 2d
|
||||
ST r32, r254, 8a, 4h
|
||||
LI64 r32, 2d
|
||||
ST r32, r254, 12a, 4h
|
||||
LD r1, r254, 0a, 16h
|
||||
LD r31, r254, 16a, 16h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 16a, 24h
|
||||
LI64 r32, 7d
|
||||
JAL r31, r0, :foo
|
||||
ST r1, r254, 0a, 16h
|
||||
LD r33, r254, 0a, 8h
|
||||
SUB64 r32, r32, r33
|
||||
JAL r31, r0, :foo
|
||||
ST r1, r254, 0a, 16h
|
||||
CP r33, r0
|
||||
LD r33, r254, 8a, 4h
|
||||
SXT32 r33, r33
|
||||
SUB64 r32, r32, r33
|
||||
JAL r31, r0, :foo
|
||||
ST r1, r254, 0a, 16h
|
||||
CP r33, r0
|
||||
LD r33, r254, 12a, 4h
|
||||
SXT32 r33, r33
|
||||
SUB64 r1, r32, r33
|
||||
LD r31, r254, 16a, 24h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 341
|
||||
ret: 0
|
||||
status: Ok(())
|
46
hblang/tests/codegen_tests_structs.txt
Normal file
46
hblang/tests/codegen_tests_structs.txt
Normal file
|
@ -0,0 +1,46 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 24a, 24h
|
||||
LI64 r32, 4d
|
||||
ST r32, r254, 0a, 8h
|
||||
LI64 r32, 1d
|
||||
ST r32, r254, 8a, 8h
|
||||
LI64 r32, 3d
|
||||
ST r32, r254, 16a, 8h
|
||||
ADDI64 r2, r254, 0d
|
||||
ADDI64 r1, r254, 0d
|
||||
JAL r31, r0, :odher_pass
|
||||
LD r32, r254, 16a, 8h
|
||||
LI64 r33, 3d
|
||||
JNE r32, r33, :0
|
||||
ADDI64 r33, r254, 0d
|
||||
CP r2, r33
|
||||
JAL r31, r0, :pass
|
||||
JMP :1
|
||||
0: LI64 r1, 0d
|
||||
1: LD r31, r254, 24a, 24h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
odher_pass:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
CP r33, r1
|
||||
CP r34, r33
|
||||
BMC r32, r34, 24h
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
pass:
|
||||
ADDI64 r254, r254, -32d
|
||||
ST r31, r254, 0a, 32h
|
||||
CP r32, r2
|
||||
LD r33, r32, 0a, 8h
|
||||
LD r34, r32, 8a, 8h
|
||||
SUB64 r1, r33, r34
|
||||
LD r31, r254, 0a, 32h
|
||||
ADDI64 r254, r254, 32d
|
||||
JALA r0, r31, 0a
|
||||
code size: 394
|
||||
ret: 3
|
||||
status: Ok(())
|
38
hblang/tests/codegen_tests_tests_ptr_to_ptr_copy.txt
Normal file
38
hblang/tests/codegen_tests_tests_ptr_to_ptr_copy.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -10272d
|
||||
ST r31, r254, 10240a, 32h
|
||||
LI64 r32, 0d
|
||||
2: LI64 r33, 1024d
|
||||
JLTS r32, r33, :0
|
||||
JMP :1
|
||||
0: ADDI64 r33, r254, 0d
|
||||
CP r34, r32
|
||||
ADD64 r33, r33, r34
|
||||
LI64 r34, 64d
|
||||
ST r34, r33, 0a, 1h
|
||||
ADDI64 r32, r32, 1d
|
||||
JMP :2
|
||||
1: LI64 r32, 1d
|
||||
5: LI64 r33, 10d
|
||||
JLTS r32, r33, :3
|
||||
JMP :4
|
||||
3: ADDI64 r33, r254, 0d
|
||||
CP r34, r32
|
||||
MULI64 r34, r34, 1024d
|
||||
ADD64 r33, r33, r34
|
||||
ADDI64 r34, r254, 0d
|
||||
BMC r34, r33, 1024h
|
||||
ADDI64 r32, r32, 1d
|
||||
JMP :5
|
||||
4: LI64 r33, 1024d
|
||||
MULI64 r33, r33, 2d
|
||||
ADDI64 r34, r254, 0d
|
||||
ADD64 r34, r34, r33
|
||||
CP r1, r0
|
||||
LD r1, r34, 0a, 1h
|
||||
LD r31, r254, 10240a, 32h
|
||||
ADDI64 r254, r254, 10272d
|
||||
JALA r0, r31, 0a
|
||||
code size: 297
|
||||
ret: 64
|
||||
status: Ok(())
|
13
hblang/tests/codegen_tests_variables.txt
Normal file
13
hblang/tests/codegen_tests_variables.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r32, 1d
|
||||
LI64 r33, 2d
|
||||
ADDI64 r32, r32, 1d
|
||||
SUB64 r1, r32, r33
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 102
|
||||
ret: 0
|
||||
status: Ok(())
|
45
hblang/tests/codegen_tests_wide_ret.txt
Normal file
45
hblang/tests/codegen_tests_wide_ret.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -48d
|
||||
ST r31, r254, 16a, 32h
|
||||
CP r32, r3
|
||||
CP r33, r4
|
||||
JAL r31, r0, :small_struct
|
||||
CP r34, r1
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 8a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 9a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 10a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 11a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 12a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 13a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 14a, 1h
|
||||
LI64 r34, 0d
|
||||
ST r34, r254, 15a, 1h
|
||||
LD r34, r254, 8a, 8h
|
||||
ST r34, r254, 0a, 8h
|
||||
ST r34, r254, 8a, 8h
|
||||
LD r1, r254, 0a, 16h
|
||||
LD r31, r254, 16a, 32h
|
||||
ADDI64 r254, r254, 48d
|
||||
JALA r0, r31, 0a
|
||||
small_struct:
|
||||
ADDI64 r254, r254, -20d
|
||||
ST r31, r254, 4a, 16h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 0a, 2h
|
||||
LI64 r32, 0d
|
||||
ST r32, r254, 2a, 2h
|
||||
CP r1, r0
|
||||
LD r1, r254, 0a, 4h
|
||||
LD r31, r254, 4a, 16h
|
||||
ADDI64 r254, r254, 20d
|
||||
JALA r0, r31, 0a
|
||||
code size: 440
|
||||
ret: 0
|
||||
status: Ok(())
|
25
hblang/tests/codegen_tests_writing_into_string.txt
Normal file
25
hblang/tests/codegen_tests_writing_into_string.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
inl:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LRA r32, r0, :"luhahah\0"
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :outl
|
||||
JAL r31, r0, :inl
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
outl:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LRA r32, r0, :"whahaha\0"
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 229
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,12 +1,10 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :some_func
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
some_func:
|
||||
JALA r0, r31, 0a
|
||||
code size: 85
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Ok(())
|
17
hblang/tests/son_tests_branch_assignments.txt
Normal file
17
hblang/tests/son_tests_branch_assignments.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
LI64 r1, 1d
|
||||
JNE r2, r1, :0
|
||||
JMP :1
|
||||
0: LI64 r32, 0d
|
||||
JNE r2, r32, :2
|
||||
LI64 r1, 2d
|
||||
JMP :1
|
||||
2: LI64 r1, 3d
|
||||
1: LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
code size: 127
|
||||
ret: 2
|
||||
status: Ok(())
|
18
hblang/tests/son_tests_comments.txt
Normal file
18
hblang/tests/son_tests_comments.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
JAL r31, r0, :foo
|
||||
CP r32, r1
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
foo:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 146
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,13 +1,10 @@
|
|||
foo:
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :foo
|
||||
CP r1, r0
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 88
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Ok(())
|
103
hblang/tests/son_tests_exhaustive_loop_testing.txt
Normal file
103
hblang/tests/son_tests_exhaustive_loop_testing.txt
Normal file
|
@ -0,0 +1,103 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -88d
|
||||
ST r31, r254, 0a, 88h
|
||||
LI64 r32, 0d
|
||||
CP r2, r32
|
||||
JAL r31, r0, :multiple_breaks
|
||||
CP r33, r1
|
||||
LI64 r34, 3d
|
||||
JEQ r33, r34, :0
|
||||
LI64 r1, 1d
|
||||
JMP :1
|
||||
0: LI64 r35, 4d
|
||||
CP r2, r35
|
||||
JAL r31, r0, :multiple_breaks
|
||||
CP r36, r1
|
||||
LI64 r37, 10d
|
||||
JEQ r36, r37, :2
|
||||
LI64 r1, 2d
|
||||
JMP :1
|
||||
2: CP r2, r32
|
||||
JAL r31, r0, :state_change_in_break
|
||||
CP r38, r1
|
||||
JEQ r38, r32, :3
|
||||
CP r1, r34
|
||||
JMP :1
|
||||
3: CP r2, r35
|
||||
JAL r31, r0, :state_change_in_break
|
||||
CP r39, r1
|
||||
JEQ r39, r37, :4
|
||||
CP r1, r35
|
||||
JMP :1
|
||||
4: CP r2, r37
|
||||
JAL r31, r0, :continue_and_state_change
|
||||
CP r40, r1
|
||||
JEQ r40, r37, :5
|
||||
LI64 r1, 5d
|
||||
JMP :1
|
||||
5: CP r2, r34
|
||||
JAL r31, r0, :continue_and_state_change
|
||||
CP r41, r1
|
||||
JEQ r41, r32, :6
|
||||
LI64 r1, 6d
|
||||
JMP :1
|
||||
6: CP r1, r32
|
||||
1: LD r31, r254, 0a, 88h
|
||||
ADDI64 r254, r254, 88d
|
||||
JALA r0, r31, 0a
|
||||
continue_and_state_change:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r1, r2
|
||||
LI64 r32, 3d
|
||||
LI64 r33, 4d
|
||||
LI64 r34, 2d
|
||||
LI64 r35, 10d
|
||||
6: JLTU r1, r35, :0
|
||||
JMP :1
|
||||
0: JNE r1, r34, :2
|
||||
CP r1, r33
|
||||
JMP :3
|
||||
2: JNE r1, r32, :4
|
||||
LI64 r1, 0d
|
||||
1: JMP :5
|
||||
4: ADDI64 r33, r1, 1d
|
||||
CP r1, r33
|
||||
3: JMP :6
|
||||
5: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
state_change_in_break:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r1, r2
|
||||
LI64 r32, 3d
|
||||
LI64 r33, 10d
|
||||
4: JLTU r1, r33, :0
|
||||
JMP :1
|
||||
0: JNE r1, r32, :2
|
||||
LI64 r1, 0d
|
||||
1: JMP :3
|
||||
2: ADDI64 r1, r1, 1d
|
||||
JMP :4
|
||||
3: LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
multiple_breaks:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
CP r1, r2
|
||||
LI64 r32, 3d
|
||||
LI64 r33, 10d
|
||||
4: JLTU r1, r33, :0
|
||||
JMP :1
|
||||
0: ADDI64 r1, r1, 1d
|
||||
JNE r1, r32, :2
|
||||
1: JMP :3
|
||||
2: JMP :4
|
||||
3: LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
code size: 704
|
||||
ret: 0
|
||||
status: Ok(())
|
60
hblang/tests/son_tests_fb_driver.txt
Normal file
60
hblang/tests/son_tests_fb_driver.txt
Normal file
|
@ -0,0 +1,60 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -88d
|
||||
ST r31, r254, 0a, 88h
|
||||
JAL r31, r0, :check_platform
|
||||
CP r32, r1
|
||||
LI64 r33, 0d
|
||||
LI64 r34, 30d
|
||||
LI64 r35, 100d
|
||||
CP r36, r33
|
||||
CP r37, r33
|
||||
CP r38, r33
|
||||
5: JLTU r37, r34, :0
|
||||
ADDI64 r36, r36, 1d
|
||||
CP r2, r33
|
||||
CP r3, r36
|
||||
CP r4, r34
|
||||
JAL r31, r0, :set_pixel
|
||||
CP r39, r1
|
||||
JEQ r39, r38, :1
|
||||
CP r1, r33
|
||||
JMP :2
|
||||
1: JNE r36, r35, :3
|
||||
CP r1, r38
|
||||
JMP :2
|
||||
3: CP r37, r33
|
||||
CP r40, r38
|
||||
JMP :4
|
||||
0: ADDI64 r40, r38, 1d
|
||||
ADDI64 r41, r37, 1d
|
||||
CP r37, r41
|
||||
4: CP r38, r40
|
||||
JMP :5
|
||||
2: LD r31, r254, 0a, 88h
|
||||
ADDI64 r254, r254, 88d
|
||||
JALA r0, r31, 0a
|
||||
set_pixel:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
MUL64 r32, r4, r3
|
||||
ADD64 r1, r32, r2
|
||||
LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
check_platform:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :x86_fb_ptr
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
x86_fb_ptr:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r1, 100d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 423
|
||||
ret: 3000
|
||||
status: Ok(())
|
30
hblang/tests/son_tests_functions.txt
Normal file
30
hblang/tests/son_tests_functions.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -24d
|
||||
ST r31, r254, 0a, 24h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :add_one
|
||||
CP r32, r1
|
||||
LI64 r2, 20d
|
||||
JAL r31, r0, :add_two
|
||||
CP r33, r1
|
||||
ADD64 r1, r33, r32
|
||||
LD r31, r254, 0a, 24h
|
||||
ADDI64 r254, r254, 24d
|
||||
JALA r0, r31, 0a
|
||||
add_two:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
ADDI64 r1, r2, 2d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
add_one:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
ADDI64 r1, r2, 1d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
code size: 251
|
||||
ret: 33
|
||||
status: Ok(())
|
|
@ -1,11 +1,10 @@
|
|||
fun:
|
||||
UN
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :fun
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
code size: 64
|
||||
JALA r0, r31, 0a
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Err(Unreachable)
|
||||
status: Ok(())
|
29
hblang/tests/son_tests_if_statements.txt
Normal file
29
hblang/tests/son_tests_if_statements.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
fib:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
CP r32, r2
|
||||
LI64 r1, 1d
|
||||
LI64 r33, 2d
|
||||
JGTU r32, r33, :0
|
||||
JMP :1
|
||||
0: SUB64 r2, r32, r1
|
||||
JAL r31, r0, :fib
|
||||
CP r34, r1
|
||||
SUB64 r2, r32, r33
|
||||
JAL r31, r0, :fib
|
||||
CP r35, r1
|
||||
ADD64 r1, r35, r34
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 208
|
||||
ret: 55
|
||||
status: Ok(())
|
28
hblang/tests/son_tests_loops.txt
Normal file
28
hblang/tests/son_tests_loops.txt
Normal file
|
@ -0,0 +1,28 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
LI64 r2, 10d
|
||||
JAL r31, r0, :fib
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
fib:
|
||||
ADDI64 r254, r254, -40d
|
||||
ST r31, r254, 0a, 40h
|
||||
LI64 r32, 1d
|
||||
LI64 r33, 0d
|
||||
CP r1, r33
|
||||
CP r34, r32
|
||||
2: JNE r2, r33, :0
|
||||
JMP :1
|
||||
0: ADD64 r35, r34, r1
|
||||
SUB64 r2, r2, r32
|
||||
CP r1, r34
|
||||
CP r34, r35
|
||||
JMP :2
|
||||
1: LD r31, r254, 0a, 40h
|
||||
ADDI64 r254, r254, 40d
|
||||
JALA r0, r31, 0a
|
||||
code size: 198
|
||||
ret: 55
|
||||
status: Ok(())
|
|
@ -1,15 +1,10 @@
|
|||
inl:
|
||||
JALA r0, r31, 0a
|
||||
main:
|
||||
ADDI64 r254, r254, -8d
|
||||
ST r31, r254, 0a, 8h
|
||||
JAL r31, r0, :outl
|
||||
JAL r31, r0, :inl
|
||||
LI64 r1, 0d
|
||||
LD r31, r254, 0a, 8h
|
||||
ADDI64 r254, r254, 8d
|
||||
JALA r0, r31, 0a
|
||||
outl:
|
||||
JALA r0, r31, 0a
|
||||
code size: 103
|
||||
code size: 77
|
||||
ret: 0
|
||||
status: Ok(())
|
11
hblang/text-prj/main.hb
Normal file
11
hblang/text-prj/main.hb
Normal file
|
@ -0,0 +1,11 @@
|
|||
foo := 0;
|
||||
|
||||
.{global, fib, Structa, create_window, WindowID} := @use("pkg.hb")
|
||||
|
||||
main := fn(a: int): int {
|
||||
g := global
|
||||
|
||||
win := create_window()
|
||||
|
||||
return fib(g + Structa.(0, 0).foo)
|
||||
}
|
19
hblang/text-prj/pkg.hb
Normal file
19
hblang/text-prj/pkg.hb
Normal file
|
@ -0,0 +1,19 @@
|
|||
global := 10
|
||||
|
||||
Structa := struct {
|
||||
foo: int,
|
||||
goo: int,
|
||||
}
|
||||
|
||||
create_window := fn(): WindowID {
|
||||
return WindowID.(1, 2)
|
||||
}
|
||||
|
||||
WindowID := struct {
|
||||
host_id: int,
|
||||
window_id: int,
|
||||
}
|
||||
|
||||
fib := fn(n: int): int {
|
||||
return n + 1
|
||||
}
|
|
@ -3,12 +3,10 @@ name = "hbvm"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
hbbytecode = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["alloc"]
|
||||
disasm = ["hbbytecode/disasm", "alloc"]
|
||||
alloc = []
|
||||
nightly = []
|
||||
|
||||
[dependencies]
|
||||
hbbytecode = { path = "../hbbytecode", default-features = false }
|
0
hbvm/README.md
Normal file
0
hbvm/README.md
Normal file
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue