Compare commits

..

15 commits

Author SHA1 Message Date
mlokr 29be2ba9c3
moving codegen to instrs alongside disasm 2024-09-19 18:25:28 +02:00
mlokr 31c501c643
merge 2024-09-19 17:15:03 +02:00
mlokr 0d118c17b2
reducing dependencies 2024-09-19 17:13:51 +02:00
mlokr e4e7f8d5b5
implementing ableos executable format 2024-09-19 13:40:03 +02:00
mlokr 4849807353
removing git support and relative path prefix which did nothing anyway 2024-09-18 10:34:07 +02:00
mlokr 6e30968c54
improving one particular error message 2024-09-18 10:14:17 +02:00
mlokr 6fc0eb3498
triing to turn absolute to relative paths in error messages 2024-09-18 10:07:40 +02:00
mlokr 98dfd6b09c
improving parser error messages 2024-09-18 09:47:52 +02:00
mlokr ece9bb8bf2
eca now infers the return type 2024-09-17 18:11:07 +02:00
mlokr 09fcbbc03b
forcing structs to always be on stack 2024-09-17 18:07:15 +02:00
mlokr a7fda408ef
forgot update 2024-09-17 17:59:32 +02:00
mlokr 5d77ae93b4
forcing structs to always be on stack 2024-09-17 17:59:03 +02:00
mlokr 4a9b9de87f
nah, lets use dummer code 2024-09-17 15:50:45 +02:00
mlokr bba3570788
adding wide return move for wider range of cases 2024-09-17 15:47:23 +02:00
mlokr 6852452f1a
fixing wide return value 2024-09-17 15:25:31 +02:00
36 changed files with 2703 additions and 1778 deletions

32
Cargo.lock generated
View file

@ -35,7 +35,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "cranelift-bforest" name = "cranelift-bforest"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
dependencies = [ dependencies = [
"cranelift-entity", "cranelift-entity",
] ]
@ -43,12 +43,12 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-bitset" name = "cranelift-bitset"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
[[package]] [[package]]
name = "cranelift-codegen" name = "cranelift-codegen"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"cranelift-bforest", "cranelift-bforest",
@ -60,7 +60,7 @@ dependencies = [
"cranelift-isle", "cranelift-isle",
"hashbrown", "hashbrown",
"log", "log",
"regalloc2", "regalloc2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hash", "rustc-hash",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
@ -69,7 +69,7 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen-meta" name = "cranelift-codegen-meta"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
dependencies = [ dependencies = [
"cranelift-codegen-shared", "cranelift-codegen-shared",
] ]
@ -77,12 +77,12 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-codegen-shared" name = "cranelift-codegen-shared"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
[[package]] [[package]]
name = "cranelift-control" name = "cranelift-control"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
] ]
@ -90,7 +90,7 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-entity" name = "cranelift-entity"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
dependencies = [ dependencies = [
"cranelift-bitset", "cranelift-bitset",
] ]
@ -98,7 +98,7 @@ dependencies = [
[[package]] [[package]]
name = "cranelift-isle" name = "cranelift-isle"
version = "0.113.0" version = "0.113.0"
source = "git+https://github.com/jakubDoka/wasmtime.git#969c85a481985be2fa2d555cdb338454f77213c6" source = "git+https://github.com/jakubDoka/wasmtime.git#a27ac7aed78fae886e45f119139a1e54b20dd523"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
@ -122,7 +122,7 @@ dependencies = [
"cranelift-control", "cranelift-control",
"cranelift-isle", "cranelift-isle",
"log", "log",
"regalloc2", "regalloc2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
] ]
@ -135,7 +135,9 @@ version = "0.1.0"
name = "hblang" name = "hblang"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hbbytecode",
"hbvm", "hbvm",
"regalloc2 0.10.2 (git+https://github.com/jakubDoka/regalloc2)",
] ]
[[package]] [[package]]
@ -211,6 +213,16 @@ dependencies = [
"smallvec", "smallvec",
] ]
[[package]]
name = "regalloc2"
version = "0.10.2"
source = "git+https://github.com/jakubDoka/regalloc2#7e74b2fde4f022816cded93ab5685e46f8e3a159"
dependencies = [
"hashbrown",
"rustc-hash",
"smallvec",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "2.0.0" version = "2.0.0"

View file

@ -2,3 +2,8 @@
name = "hbbytecode" name = "hbbytecode"
version = "0.1.0" version = "0.1.0"
edition = "2018" edition = "2018"
[features]
default = ["disasm"]
std = []
disasm = ["std"]

View file

@ -1,24 +1,24 @@
#![feature(iter_next_chunk)] #![feature(iter_next_chunk)]
use std::{collections::HashSet, fmt::Write};
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=instructions.in"); println!("cargo:rerun-if-changed=instructions.in");
let mut generated = String::new(); let mut generated = String::new();
gen_op_structs(&mut generated)?; gen_instrs(&mut generated)?;
std::fs::write("src/ops.rs", generated)?; std::fs::write("src/instrs.rs", generated)?;
let mut generated = String::new();
gen_op_codes(&mut generated)?;
std::fs::write("src/opcode.rs", generated)?;
Ok(()) Ok(())
} }
fn gen_op_structs(generated: &mut String) -> std::fmt::Result { fn gen_instrs(generated: &mut String) -> Result<(), Box<dyn std::error::Error>> {
use std::fmt::Write; writeln!(generated, "#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]")?;
let mut seen = std::collections::HashSet::new();
writeln!(generated, "use crate::*;")?; writeln!(generated, "use crate::*;")?;
'_opcode_structs: {
let mut seen = HashSet::new();
for [.., args, _] in instructions() { for [.., args, _] in instructions() {
if !seen.insert(args) { if !seen.insert(args) {
continue; continue;
@ -37,22 +37,172 @@ fn gen_op_structs(generated: &mut String) -> std::fmt::Result {
writeln!(generated, ");")?; writeln!(generated, ");")?;
writeln!(generated, "unsafe impl BytecodeItem for Ops{args} {{}}")?; writeln!(generated, "unsafe impl BytecodeItem for Ops{args} {{}}")?;
} }
}
'_max_size: {
let max = instructions()
.map(
|[_, _, ty, _]| {
if ty == "N" {
1
} else {
iter_args(ty).map(arg_to_width).sum::<usize>() + 1
}
},
)
.max()
.unwrap();
writeln!(generated, "pub const MAX_SIZE: usize = {max};")?;
}
'_encoders: {
for [op, name, ty, doc] in instructions() {
writeln!(generated, "/// {}", doc.trim_matches('"'))?;
let name = name.to_lowercase();
let args = comma_sep(
iter_args(ty)
.enumerate()
.map(|(i, c)| format!("{}{i}: {}", arg_to_name(c), arg_to_type(c))),
);
writeln!(generated, "pub fn {name}({args}) -> (usize, [u8; MAX_SIZE]) {{")?;
let arg_names =
comma_sep(iter_args(ty).enumerate().map(|(i, c)| format!("{}{i}", arg_to_name(c))));
writeln!(generated, " unsafe {{ crate::encode({ty}({op}, {arg_names})) }}")?;
writeln!(generated, "}}")?;
}
}
'_structs: {
let mut seen = std::collections::HashSet::new();
for [_, _, ty, _] in instructions() {
if !seen.insert(ty) {
continue;
}
let types = comma_sep(iter_args(ty).map(arg_to_type).map(|s| s.to_string()));
writeln!(generated, "#[repr(packed)] pub struct {ty}(u8, {types});")?;
}
}
'_name_list: {
writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?;
for [_, name, _, _] in instructions() {
writeln!(generated, " \"{}\",", name.to_lowercase())?;
}
writeln!(generated, "];")?;
}
let instr = "Instr";
let oper = "Oper";
'_instr_enum: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)]")?;
writeln!(generated, "pub enum {instr} {{")?;
for [id, name, ..] in instructions() {
writeln!(generated, " {name} = {id},")?;
}
writeln!(generated, "}}")?;
}
'_arg_kind: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]")?;
writeln!(generated, "pub enum {oper} {{")?;
let mut seen = HashSet::new();
for ty in instructions().flat_map(|[.., ty, _]| iter_args(ty)) {
if !seen.insert(ty) {
continue;
}
writeln!(generated, " {ty}({}),", arg_to_type(ty))?;
}
writeln!(generated, "}}")?;
}
'_parse_opers: {
writeln!(
generated,
"/// 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 std::vec::Vec<{oper}>) -> Option<()> {{")?;
writeln!(generated, " match kind {{")?;
let mut instrs = instructions().collect::<Vec<_>>();
instrs.sort_unstable_by_key(|&[.., ty, _]| ty);
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| a == b) {
let ty = group[0][2];
for &[_, name, ..] in group {
writeln!(generated, " | {instr}::{name}")?;
}
generated.pop();
writeln!(generated, " => {{")?;
if iter_args(ty).count() != 0 {
writeln!(generated, " let data = crate::decode::<{ty}>(bytes)?;")?;
writeln!(
generated,
" buf.extend([{}]);",
comma_sep(
iter_args(ty).zip(1u32..).map(|(t, i)| format!("{oper}::{t}(data.{i})"))
)
)?;
} else {
writeln!(generated, " crate::decode::<{ty}>(bytes)?;")?;
}
writeln!(generated, " }}")?;
}
writeln!(generated, " }}")?;
writeln!(generated, " Some(())")?;
writeln!(generated, "}}")?;
}
std::fs::write("src/instrs.rs", generated)?;
Ok(()) Ok(())
} }
fn gen_op_codes(generated: &mut String) -> std::fmt::Result { fn comma_sep(items: impl Iterator<Item = String>) -> String {
use std::fmt::Write; items.map(|item| item.to_string()).collect::<Vec<_>>().join(", ")
for [op, name, _, comment] in instructions() {
writeln!(generated, "#[doc = {comment}]")?;
writeln!(generated, "pub const {name}: u8 = {op};")?;
}
Ok(())
} }
fn instructions() -> impl Iterator<Item = [&'static str; 4]> { fn instructions() -> impl Iterator<Item = [&'static str; 4]> {
include_str!("../hbbytecode/instructions.in") include_str!("../hbbytecode/instructions.in")
.lines() .lines()
.map(|line| line.strip_suffix(';').unwrap()) .filter_map(|line| line.strip_suffix(';'))
.map(|line| line.splitn(4, ',').map(str::trim).next_chunk().unwrap()) .map(|line| line.splitn(4, ',').map(str::trim).next_chunk().unwrap())
} }
fn arg_to_type(arg: char) -> &'static str {
match arg {
'R' | 'B' => "u8",
'H' => "u16",
'W' => "u32",
'D' | 'A' => "u64",
'P' => "i16",
'O' => "i32",
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_width(arg: char) -> usize {
match arg {
'R' | 'B' => 1,
'H' => 2,
'W' => 4,
'D' | 'A' => 8,
'P' => 2,
'O' => 4,
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_name(arg: char) -> &'static str {
match arg {
'R' => "reg",
'B' | 'H' | 'W' | 'D' => "imm",
'P' | 'O' => "offset",
'A' => "addr",
_ => panic!("unknown type: {}", arg),
}
}
fn iter_args(ty: &'static str) -> impl Iterator<Item = char> {
ty.chars().filter(|c| *c != 'N')
}

1025
hbbytecode/src/instrs.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,12 @@
#![no_std] #![no_std]
pub use crate::ops::*; #[cfg(feature = "std")]
extern crate std;
pub use crate::instrs::*;
use core::convert::TryFrom; use core::convert::TryFrom;
pub mod opcode; mod instrs;
mod ops;
type OpR = u8; type OpR = u8;
@ -22,6 +24,38 @@ type OpD = u64;
pub unsafe trait BytecodeItem {} pub unsafe trait BytecodeItem {}
unsafe impl BytecodeItem for u8 {} unsafe impl BytecodeItem for u8 {}
impl TryFrom<u8> for Instr {
type Error = u8;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
#[cold]
fn failed(value: u8) -> Result<Instr, u8> {
Err(value)
}
if value < NAMES.len() as u8 {
unsafe { Ok(std::mem::transmute::<u8, Instr>(value)) }
} else {
failed(value)
}
}
}
#[inline]
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
let mut buf = [0; instrs::MAX_SIZE];
core::ptr::write(buf.as_mut_ptr() as *mut T, instr);
(core::mem::size_of::<T>(), buf)
}
#[inline]
fn decode<T>(binary: &mut &[u8]) -> Option<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)) }
}
/// Rounding mode /// Rounding mode
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)] #[repr(u8)]
@ -39,3 +73,181 @@ impl TryFrom<u8> for RoundingMode {
(value <= 3).then(|| unsafe { core::mem::transmute(value) }).ok_or(()) (value <= 3).then(|| unsafe { core::mem::transmute(value) }).ok_or(())
} }
} }
#[cfg(feature = "disasm")]
#[derive(Clone, Copy)]
pub enum DisasmItem {
Func,
Global,
}
#[cfg(feature = "disasm")]
pub fn disasm(
binary: &mut &[u8],
functions: &std::collections::BTreeMap<u32, (&str, u32, DisasmItem)>,
out: &mut impl std::io::Write,
mut eca_handler: impl FnMut(&mut &[u8]),
) -> std::io::Result<()> {
use {
self::instrs::Instr,
std::{
collections::{hash_map::Entry, HashMap},
convert::TryInto,
vec::Vec,
},
};
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 = 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() {
if matches!(kind, DisasmItem::Global) {
continue;
}
let prev = *binary;
*binary = &binary[..off as usize];
let mut label_count = 0;
while let Some(&byte) = binary.first() {
let offset: i32 = (prev.len() - binary.len()).try_into().unwrap();
if offset as u32 == off + len {
break;
}
let Ok(inst) = instr_from_byte(byte) else { break };
instrs::parse_args(binary, inst, &mut buf).ok_or(std::io::ErrorKind::OutOfMemory)?;
for op in buf.drain(..) {
let rel = match op {
instrs::Oper::O(rel) => rel,
instrs::Oper::P(rel) => rel.into(),
_ => continue,
};
has_cycle |= rel == 0;
let global_offset: u32 = (offset + rel).try_into().unwrap();
if functions.get(&global_offset).is_some() {
continue;
}
label_count += match labels.entry(global_offset) {
Entry::Occupied(_) => 0,
Entry::Vacant(entry) => {
entry.insert(label_count);
1
}
}
}
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
}
*binary = prev;
}
let mut ordered = functions.iter().collect::<Vec<_>>();
ordered.sort_unstable_by_key(|(_, (name, _, _))| name);
'_dump: for (&off, &(name, len, kind)) in ordered {
if matches!(kind, DisasmItem::Global) {
continue;
}
let prev = *binary;
writeln!(out, "{name}:")?;
*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 {
break;
}
let Ok(inst) = instr_from_byte(byte) else {
writeln!(out, "invalid instr {byte}")?;
break;
};
instrs::parse_args(binary, inst, &mut buf).unwrap();
if let Some(label) = labels.get(&offset.try_into().unwrap()) {
write!(out, "{:>2}: ", label)?;
} else {
write!(out, " ")?;
}
write!(out, "{inst:<8?} ")?;
'a: for (i, op) in buf.drain(..).enumerate() {
if i != 0 {
write!(out, ", ")?;
}
let rel = 'b: {
match op {
instrs::Oper::O(rel) => break 'b rel,
instrs::Oper::P(rel) => break 'b rel.into(),
instrs::Oper::R(r) => write!(out, "r{r}")?,
instrs::Oper::B(b) => write!(out, "{b}b")?,
instrs::Oper::H(h) => write!(out, "{h}h")?,
instrs::Oper::W(w) => write!(out, "{w}w")?,
instrs::Oper::D(d) if (d as i64) < 0 => write!(out, "{}d", d as i64)?,
instrs::Oper::D(d) => write!(out, "{d}d")?,
instrs::Oper::A(a) => write!(out, "{a}a")?,
}
continue 'a;
};
let global_offset: u32 = (offset + rel).try_into().unwrap();
if let Some(&(name, ..)) = functions.get(&global_offset) {
if name.contains('\0') {
write!(out, ":{name:?}")?;
} else {
write!(out, ":{name}")?;
}
} else {
let local_has_oob = global_offset < off
|| global_offset > off + len
|| 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 {
write!(out, "!!!!!!!!!{rel}")?;
} else {
write!(out, ":{label}")?;
}
}
}
writeln!(out)?;
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
}
*binary = prev;
}
if has_oob {
return Err(std::io::ErrorKind::InvalidInput.into());
}
if has_cycle {
return Err(std::io::ErrorKind::TimedOut.into());
}
Ok(())
}

View file

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cranelift-codegen = { git = "https://github.com/jakubDoka/wasmtime.git", default-features = false } cranelift-codegen = { git = "https://github.com/jakubDoka/wasmtime.git", default-features = false, features = ["std"] }
cranelift-control = { git = "https://github.com/jakubDoka/wasmtime.git", default-features = false } cranelift-control = { git = "https://github.com/jakubDoka/wasmtime.git", default-features = false }
log = "0.4.22" log = "0.4.22"
regalloc2 = "0.10.2" regalloc2 = "0.10.2"

View file

@ -15,7 +15,6 @@
// current directory is used to find the sources. // current directory is used to find the sources.
use { use {
core::panic,
cranelift_codegen_meta::{self as meta, isle::IsleCompilations}, cranelift_codegen_meta::{self as meta, isle::IsleCompilations},
cranelift_isle::error::Errors, cranelift_isle::error::Errors,
meta::isle::IsleCompilation, meta::isle::IsleCompilation,

View file

@ -1,13 +1,15 @@
//! Implementation of a standard Riscv64 ABI. //! Implementation of a standard Riscv64 ABI.
use { use {
crate::inst::*, crate::{
inst::*,
settings::{self, Flags as RiscvFlags},
},
alloc::{boxed::Box, vec::Vec}, alloc::{boxed::Box, vec::Vec},
cranelift_codegen::{ cranelift_codegen::{
ir::{self, types::*, LibCall, Signature}, ir::{self, types::*, LibCall, Signature},
isa::{self, unwind::UnwindInst, CallConv}, isa::{self, unwind::UnwindInst, CallConv},
machinst::*, machinst::*,
settings::{self, Flags as RiscvFlags},
CodegenError, CodegenResult, CodegenError, CodegenResult,
}, },
regalloc2::{MachineEnv, PReg, PRegSet}, regalloc2::{MachineEnv, PReg, PRegSet},
@ -21,11 +23,6 @@ pub(crate) type Riscv64Callee = Callee<Riscv64MachineDeps>;
/// Support for the Riscv64 ABI from the caller side (at a callsite). /// Support for the Riscv64 ABI from the caller side (at a callsite).
pub(crate) type Riscv64ABICallSite = CallSite<Riscv64MachineDeps>; pub(crate) type Riscv64ABICallSite = CallSite<Riscv64MachineDeps>;
/// This is the limit for the size of argument and return-value areas on the
/// stack. We place a reasonable limit here to avoid integer overflow issues
/// with 32-bit arithmetic: for now, 128 MB.
static STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
/// Riscv64-specific ABI behavior. This struct just serves as an implementation /// Riscv64-specific ABI behavior. This struct just serves as an implementation
/// point for the trait; it is never actually instantiated. /// point for the trait; it is never actually instantiated.
pub struct Riscv64MachineDeps; pub struct Riscv64MachineDeps;
@ -70,6 +67,11 @@ impl ABIMachineSpec for Riscv64MachineDeps {
type F = RiscvFlags; type F = RiscvFlags;
type I = Inst; type I = Inst;
/// This is the limit for the size of argument and return-value areas on the
/// stack. We place a reasonable limit here to avoid integer overflow issues
/// with 32-bit arithmetic: for now, 128 MB.
const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
fn word_bits() -> u32 { fn word_bits() -> u32 {
64 64
} }
@ -651,8 +653,12 @@ impl ABIMachineSpec for Riscv64MachineDeps {
} }
} }
impl Riscv64ABICallSite { pub trait EmitReturnCall {
pub fn emit_return_call(mut self, ctx: &mut Lower<Inst>, args: isle::ValueSlice) { fn emit_return_call(mut self, ctx: &mut Lower<Inst>, args: isle::ValueSlice);
}
impl EmitReturnCall for Riscv64ABICallSite {
fn emit_return_call(mut self, ctx: &mut Lower<Inst>, args: isle::ValueSlice) {
let new_stack_arg_size = let new_stack_arg_size =
u32::try_from(self.sig(ctx.sigs()).sized_stack_arg_space()).unwrap(); u32::try_from(self.sig(ctx.sigs()).sized_stack_arg_space()).unwrap();

View file

@ -6,12 +6,14 @@
//! Some instructions especially in extensions have slight variations from //! Some instructions especially in extensions have slight variations from
//! the base RISC-V specification. //! the base RISC-V specification.
use super::*; use {
use crate::lower::isle::generated_code::{ super::*,
crate::lower::isle::generated_code::{
COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, FpuOPWidth, COpcodeSpace, CaOp, CbOp, CiOp, CiwOp, ClOp, CrOp, CsOp, CssOp, CsznOp, FpuOPWidth,
VecAluOpRImm5, VecAluOpRR, VecAluOpRRRImm5, VecAluOpRRRR, VecOpCategory, ZcbMemOp, VecAluOpRImm5, VecAluOpRR, VecAluOpRRRImm5, VecAluOpRRRR, VecOpCategory, ZcbMemOp,
},
cranelift_codegen::machinst::isle::WritableReg,
}; };
use crate::machinst::isle::WritableReg;
fn unsigned_field_width(value: u32, width: u8) -> u32 { fn unsigned_field_width(value: u32, width: u8) -> u32 {
debug_assert_eq!(value & (!0 << width), 0); debug_assert_eq!(value & (!0 << width), 0);
@ -199,14 +201,7 @@ pub fn encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMa
(reg_to_gpr_num(vs), op.aux_encoding()) (reg_to_gpr_num(vs), op.aux_encoding())
}; };
encode_r_type_bits( encode_r_type_bits(op.opcode(), reg_to_gpr_num(vd.to_reg()), op.funct3(), vs1, vs2, funct7)
op.opcode(),
reg_to_gpr_num(vd.to_reg()),
op.funct3(),
vs1,
vs2,
funct7,
)
} }
pub fn encode_valu_r_imm( pub fn encode_valu_r_imm(
@ -222,14 +217,7 @@ pub fn encode_valu_r_imm(
let vs1 = imm.bits() as u32; let vs1 = imm.bits() as u32;
let vs2 = op.aux_encoding(); let vs2 = op.aux_encoding();
encode_r_type_bits( encode_r_type_bits(op.opcode(), reg_to_gpr_num(vd.to_reg()), op.funct3(), vs1, vs2, funct7)
op.opcode(),
reg_to_gpr_num(vd.to_reg()),
op.funct3(),
vs1,
vs2,
funct7,
)
} }
/// Encodes a Vector CFG Imm instruction. /// Encodes a Vector CFG Imm instruction.

View file

@ -1,12 +1,11 @@
//! Riscv64 ISA definitions: registers. //! Riscv64 ISA definitions: registers.
//! //!
use crate::machinst::{Reg, Writable}; use {
alloc::{vec, vec::Vec},
use alloc::vec; cranelift_codegen::machinst::{Reg, Writable},
use alloc::vec::Vec; regalloc2::{PReg, RegClass, VReg},
};
use regalloc2::{PReg, RegClass, VReg};
// first argument of function call // first argument of function call
#[inline] #[inline]

View file

@ -1,19 +1,20 @@
use crate::lower::isle::generated_code::VecAluOpRRRR; use {
use crate::lower::isle::generated_code::{ super::{Type, UImm5},
VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAvl, crate::{
VecElementWidth, VecLmul, VecMaskMode, VecOpCategory, VecOpMasking, VecTailMode, lower::isle::generated_code::{
VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5,
VecAluOpRRRR, VecAvl, VecElementWidth, VecLmul, VecMaskMode, VecOpCategory,
VecOpMasking, VecTailMode,
},
Reg,
},
core::fmt,
cranelift_codegen::machinst::{OperandVisitor, RegClass},
}; };
use crate::machinst::{OperandVisitor, RegClass};
use crate::Reg;
use core::fmt;
use super::{Type, UImm5};
impl VecAvl { impl VecAvl {
pub fn _static(size: u32) -> Self { pub fn _static(size: u32) -> Self {
VecAvl::Static { VecAvl::Static { size: UImm5::maybe_from_u8(size as u8).expect("Invalid size for AVL") }
size: UImm5::maybe_from_u8(size as u8).expect("Invalid size for AVL"),
}
} }
pub fn is_static(&self) -> bool { pub fn is_static(&self) -> bool {
@ -178,11 +179,7 @@ impl VType {
impl fmt::Display for VType { impl fmt::Display for VType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(f, "{}, {}, {}, {}", self.sew, self.lmul, self.tail_mode, self.mask_mode)
f,
"{}, {}, {}, {}",
self.sew, self.lmul, self.tail_mode, self.mask_mode
)
} }
} }
@ -255,6 +252,7 @@ impl VecAluOpRRRR {
// Vector Opcode // Vector Opcode
0x57 0x57
} }
pub fn funct3(&self) -> u32 { pub fn funct3(&self) -> u32 {
self.category().encode() self.category().encode()
} }
@ -323,6 +321,7 @@ impl VecAluOpRRRImm5 {
// Vector Opcode // Vector Opcode
0x57 0x57
} }
pub fn funct3(&self) -> u32 { pub fn funct3(&self) -> u32 {
self.category().encode() self.category().encode()
} }
@ -369,9 +368,11 @@ impl VecAluOpRRR {
// Vector Opcode // Vector Opcode
0x57 0x57
} }
pub fn funct3(&self) -> u32 { pub fn funct3(&self) -> u32 {
self.category().encode() self.category().encode()
} }
pub fn funct6(&self) -> u32 { pub fn funct6(&self) -> u32 {
// See: https://github.com/riscv/riscv-v-spec/blob/master/inst-table.adoc // See: https://github.com/riscv/riscv-v-spec/blob/master/inst-table.adoc
match self { match self {
@ -658,6 +659,7 @@ impl VecAluOpRRImm5 {
// Vector Opcode // Vector Opcode
0x57 0x57
} }
pub fn funct3(&self) -> u32 { pub fn funct3(&self) -> u32 {
self.category().encode() self.category().encode()
} }
@ -1016,6 +1018,7 @@ impl VecAluOpRImm5 {
// Vector Opcode // Vector Opcode
0x57 0x57
} }
pub fn funct3(&self) -> u32 { pub fn funct3(&self) -> u32 {
self.category().encode() self.category().encode()
} }

View file

@ -8,4 +8,6 @@ name = "hbc"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
hbbytecode = { version = "0.1.0", path = "../hbbytecode" }
hbvm = { path = "../hbvm", features = ["nightly"] } hbvm = { path = "../hbvm", features = ["nightly"] }
regalloc2 = { git = "https://github.com/jakubDoka/regalloc2" }

View file

@ -237,7 +237,7 @@ main := fn(): int {
size_of_Type_in_bytes := @sizeof(foo.Type) size_of_Type_in_bytes := @sizeof(foo.Type)
align_of_Type_in_bytes := @alignof(foo.Type) align_of_Type_in_bytes := @alignof(foo.Type)
hardcoded_pointer := @as(^u8, @bitcast(10)) hardcoded_pointer := @as(^u8, @bitcast(10))
ecall_that_returns_int := @eca(int, 1, foo.Type.(10, 20), 5, 6) ecall_that_returns_int := @as(int, @eca(1, foo.Type.(10, 20), 5, 6))
return @inline(foo.foo) return @inline(foo.foo)
} }
@ -356,6 +356,29 @@ main := fn(): int {
} }
``` ```
#### 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 ### Incomplete Examples
#### comptime_pointers #### comptime_pointers
@ -376,8 +399,8 @@ modify := fn($num: ^int): void {
MALLOC_SYS_CALL := 69 MALLOC_SYS_CALL := 69
FREE_SYS_CALL := 96 FREE_SYS_CALL := 96
malloc := fn(size: uint, align: uint): ^void return @eca(^void, MALLOC_SYS_CALL, size, align) 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(void, FREE_SYS_CALL, ptr, size, align) free := fn(ptr: ^void, size: uint, align: uint): void return @eca(FREE_SYS_CALL, ptr, size, align)
Vec := fn($Elem: type): type { Vec := fn($Elem: type): type {
return struct { return struct {
@ -750,7 +773,7 @@ screenidx := fn(orange: int): int {
// in module: random.hb // in module: random.hb
integer := fn(min: int, max: int): int { integer := fn(min: int, max: int): int {
rng := @eca(int, 3, 4) rng := @as(int, @eca(3, 4))
if min != 0 | max != 0 { if min != 0 | max != 0 {
return rng % (max - min + 1) + min return rng % (max - min + 1) + min
@ -781,7 +804,7 @@ main := fn(): void {
// in module: random.hb // in module: random.hb
integer_range := fn(min: uint, max: int): uint { integer_range := fn(min: uint, max: int): uint {
return @eca(uint, 3, 4) % (@bitcast(max) - min + 1) + min return @eca(3, 4) % (@bitcast(max) - min + 1) + min
} }
``` ```
@ -876,7 +899,7 @@ request_page := fn(page_count: u8): ^u8 {
msg := "\{00}\{01}xxxxxxxx\0" msg := "\{00}\{01}xxxxxxxx\0"
msg_page_count := msg + 1; msg_page_count := msg + 1;
*msg_page_count = page_count *msg_page_count = page_count
return @eca(^u8, 3, 2, msg, 12) return @eca(3, 2, msg, 12)
} }
create_back_buffer := fn(total_pages: int): ^u32 { create_back_buffer := fn(total_pages: int): ^u32 {

View file

@ -1,183 +0,0 @@
#![feature(iter_next_chunk)]
use std::{collections::HashSet, fmt::Write};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../hbbytecode/instructions.in");
gen_instrs()?;
Ok(())
}
fn gen_instrs() -> Result<(), Box<dyn std::error::Error>> {
let mut generated = String::new();
writeln!(generated, "#![allow(dead_code)] #![allow(clippy::upper_case_acronyms)]")?;
'_max_size: {
let max = instructions()
.map(
|[_, _, ty, _]| {
if ty == "N" {
1
} else {
iter_args(ty).map(arg_to_width).sum::<usize>() + 1
}
},
)
.max()
.unwrap();
writeln!(generated, "pub const MAX_SIZE: usize = {max};")?;
}
'_encoders: {
for [op, name, ty, doc] in instructions() {
writeln!(generated, "/// {}", doc.trim_matches('"'))?;
let name = name.to_lowercase();
let args = comma_sep(
iter_args(ty)
.enumerate()
.map(|(i, c)| format!("{}{i}: {}", arg_to_name(c), arg_to_type(c))),
);
writeln!(generated, "pub fn {name}({args}) -> (usize, [u8; MAX_SIZE]) {{")?;
let arg_names =
comma_sep(iter_args(ty).enumerate().map(|(i, c)| format!("{}{i}", arg_to_name(c))));
writeln!(generated, " unsafe {{ crate::encode({ty}({op}, {arg_names})) }}")?;
writeln!(generated, "}}")?;
}
}
'_structs: {
let mut seen = std::collections::HashSet::new();
for [_, _, ty, _] in instructions() {
if !seen.insert(ty) {
continue;
}
let types = comma_sep(iter_args(ty).map(arg_to_type).map(|s| s.to_string()));
writeln!(generated, "#[repr(packed)] pub struct {ty}(u8, {types});")?;
}
}
'_name_list: {
writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?;
for [_, name, _, _] in instructions() {
writeln!(generated, " \"{}\",", name.to_lowercase())?;
}
writeln!(generated, "];")?;
}
let instr = "Instr";
let oper = "Oper";
'_instr_enum: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)]")?;
writeln!(generated, "pub enum {instr} {{")?;
for [id, name, ..] in instructions() {
writeln!(generated, " {name} = {id},")?;
}
writeln!(generated, "}}")?;
}
'_arg_kind: {
writeln!(generated, "#[derive(Debug, Clone, Copy, PartialEq, Eq)]")?;
writeln!(generated, "pub enum {oper} {{")?;
let mut seen = HashSet::new();
for ty in instructions().flat_map(|[.., ty, _]| iter_args(ty)) {
if !seen.insert(ty) {
continue;
}
writeln!(generated, " {ty}({}),", arg_to_type(ty))?;
}
writeln!(generated, "}}")?;
}
'_parse_opers: {
writeln!(
generated,
"/// This assumes the instruction byte is still at the beginning of the buffer"
)?;
writeln!(generated, "pub fn parse_args(bytes: &mut &[u8], kind: {instr}, buf: &mut Vec<{oper}>) -> Option<()> {{")?;
writeln!(generated, " match kind {{")?;
let mut instrs = instructions().collect::<Vec<_>>();
instrs.sort_unstable_by_key(|&[.., ty, _]| ty);
for group in instrs.chunk_by(|[.., a, _], [.., b, _]| a == b) {
let ty = group[0][2];
for &[_, name, ..] in group {
writeln!(generated, " | {instr}::{name}")?;
}
generated.pop();
writeln!(generated, " => {{")?;
if iter_args(ty).count() != 0 {
writeln!(generated, " let data = crate::decode::<{ty}>(bytes)?;")?;
writeln!(
generated,
" buf.extend([{}]);",
comma_sep(
iter_args(ty).zip(1u32..).map(|(t, i)| format!("{oper}::{t}(data.{i})"))
)
)?;
} else {
writeln!(generated, " crate::decode::<{ty}>(bytes)?;")?;
}
writeln!(generated, " }}")?;
}
writeln!(generated, " }}")?;
writeln!(generated, " Some(())")?;
writeln!(generated, "}}")?;
}
std::fs::write("src/instrs.rs", generated)?;
Ok(())
}
fn comma_sep(items: impl Iterator<Item = String>) -> String {
items.map(|item| item.to_string()).collect::<Vec<_>>().join(", ")
}
fn instructions() -> impl Iterator<Item = [&'static str; 4]> {
include_str!("../hbbytecode/instructions.in")
.lines()
.filter_map(|line| line.strip_suffix(';'))
.map(|line| line.splitn(4, ',').map(str::trim).next_chunk().unwrap())
}
fn arg_to_type(arg: char) -> &'static str {
match arg {
'R' | 'B' => "u8",
'H' => "u16",
'W' => "u32",
'D' | 'A' => "u64",
'P' => "i16",
'O' => "i32",
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_width(arg: char) -> usize {
match arg {
'R' | 'B' => 1,
'H' => 2,
'W' => 4,
'D' | 'A' => 8,
'P' => 2,
'O' => 4,
_ => panic!("unknown type: {}", arg),
}
}
fn arg_to_name(arg: char) -> &'static str {
match arg {
'R' => "reg",
'B' | 'H' | 'W' | 'D' => "imm",
'P' | 'O' => "offset",
'A' => "addr",
_ => panic!("unknown type: {}", arg),
}
}
fn iter_args(ty: &'static str) -> impl Iterator<Item = char> {
ty.chars().filter(|c| *c != 'N')
}

View file

@ -898,13 +898,21 @@ impl Codegen {
E::Directive { name: "TypeOf", args: [expr], .. } => { E::Directive { name: "TypeOf", args: [expr], .. } => {
Some(Value::ty(self.infer_type(expr))) Some(Value::ty(self.infer_type(expr)))
} }
E::Directive { name: "eca", args: [ret_ty, args @ ..], .. } => { E::Directive { name: "eca", args, pos } => {
let ty = self.ty(ret_ty); let Some(ty) = ctx.ty else {
self.report(
pos,
"type to return form eca is unknown, use `@as(<type>, @eca(...<expr>))`",
);
};
let mut parama = self.tys.parama(ty); let mut parama = self.tys.parama(ty);
let base = self.pool.arg_locs.len(); let base = self.pool.arg_locs.len();
for arg in args { for arg in args {
let arg = self.expr(arg)?; let arg = self.expr(arg)?;
if arg.ty == ty::Id::from(ty::TYPE) {
self.report(pos, "na na na nana, no passing types to ecas");
}
self.pass_arg(&arg, &mut parama); self.pass_arg(&arg, &mut parama);
self.pool.arg_locs.push(arg.loc); self.pool.arg_locs.push(arg.loc);
} }
@ -932,7 +940,7 @@ impl Codegen {
let Some(ty) = ctx.ty else { let Some(ty) = ctx.ty else {
self.report( self.report(
expr.pos(), expr.pos(),
"type to cast to is unknown, use `@as(<type>, <expr>)`", "type to cast to is unknown, use `@as(<type>, @intcast(<expr>))`",
); );
}; };
let mut val = self.expr(val)?; let mut val = self.expr(val)?;
@ -1086,7 +1094,7 @@ impl Codegen {
Some(Value::new(self.tys.make_ptr(ty::U8.into()), reg)) Some(Value::new(self.tys.make_ptr(ty::U8.into()), reg))
} }
E::Ctor { pos, ty, fields, .. } => { E::Ctor { pos, ty, fields, .. } => {
let (ty, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); let (ty, loc) = self.prepare_struct_ctor(pos, &mut ctx, ty, fields.len());
let ty::Kind::Struct(stru) = ty.expand() else { let ty::Kind::Struct(stru) = ty.expand() else {
self.report( self.report(
@ -1103,10 +1111,16 @@ impl Codegen {
let value = self.expr_ctx(value, Ctx::default().with_loc(loc).with_ty(ty))?; let value = self.expr_ctx(value, Ctx::default().with_loc(loc).with_ty(ty))?;
self.ci.free_loc(value.loc); self.ci.free_loc(value.loc);
} }
if let Some(dst_loc) = ctx.loc {
self.store_typed(loc, &dst_loc, ty);
return Some(Value { ty, loc: dst_loc });
} else {
return Some(Value { ty, loc }); return Some(Value { ty, loc });
} }
}
E::Tupl { pos, ty, fields, .. } => { E::Tupl { pos, ty, fields, .. } => {
let (ty, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); let (ty, loc) = self.prepare_struct_ctor(pos, &mut ctx, ty, fields.len());
match ty.expand() { match ty.expand() {
ty::Kind::Struct(stru) => { ty::Kind::Struct(stru) => {
@ -1141,8 +1155,13 @@ impl Codegen {
), ),
} }
if let Some(dst_loc) = ctx.loc {
self.store_typed(loc, &dst_loc, ty);
return Some(Value { ty, loc: dst_loc });
} else {
return Some(Value { ty, loc }); return Some(Value { ty, loc });
} }
}
E::Field { target, name: field } => { E::Field { target, name: field } => {
let checkpoint = self.ci.snap(); let checkpoint = self.ci.snap();
let mut tal = self.expr(target)?; let mut tal = self.expr(target)?;
@ -1369,7 +1388,6 @@ impl Codegen {
tk => Some(Value::ty(tk.compress())), tk => Some(Value::ty(tk.compress())),
}, },
E::Return { pos, val, .. } => { E::Return { pos, val, .. } => {
let ty = if let Some(val) = val {
let size = self.ci.ret.map_or(17, |ty| self.tys.size_of(ty)); let size = self.ci.ret.map_or(17, |ty| self.tys.size_of(ty));
let loc = match size { let loc = match size {
_ if self.ci.inline_ret_loc != Loc::default() => { _ if self.ci.inline_ret_loc != Loc::default() => {
@ -1379,14 +1397,15 @@ impl Codegen {
1..=16 => Some(Loc::reg(1)), 1..=16 => Some(Loc::reg(1)),
_ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()), _ => Some(Loc::reg(self.ci.ret_reg.as_ref()).into_derefed()),
}; };
self.expr_ctx(val, Ctx { ty: self.ci.ret, loc })?.ty let value = if let Some(val) = val {
self.expr_ctx(val, Ctx { ty: self.ci.ret, loc })?
} else { } else {
ty::VOID.into() Value::void()
}; };
match self.ci.ret { match self.ci.ret {
None => self.ci.ret = Some(ty), None => self.ci.ret = Some(value.ty),
Some(ret) => _ = self.assert_ty(pos, ty, ret, "return type"), Some(ret) => _ = self.assert_ty(pos, value.ty, ret, "return type"),
} }
self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4));
@ -1891,7 +1910,7 @@ impl Codegen {
fn prepare_struct_ctor( fn prepare_struct_ctor(
&mut self, &mut self,
pos: Pos, pos: Pos,
ctx: Ctx, ctx: &mut Ctx,
ty: Option<&Expr>, ty: Option<&Expr>,
field_len: usize, field_len: usize,
) -> (ty::Id, Loc) { ) -> (ty::Id, Loc) {
@ -1927,8 +1946,11 @@ impl Codegen {
} }
let size = self.tys.size_of(ty); let size = self.tys.size_of(ty);
let loc = ctx.loc.unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))); if ctx.loc.as_ref().map_or(true, |l| l.is_reg()) {
(ty, loc) (ty, Loc::stack(self.ci.stack.allocate(size)))
} else {
(ty, ctx.loc.take().unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))))
}
} }
fn struct_op( fn struct_op(
@ -2278,6 +2300,25 @@ impl Codegen {
self.ci.emit(cp(dst.get(), src.get())); self.ci.emit(cp(dst.get(), src.get()));
} }
} }
//(lpat!(false, src, 0, None), lpat!(false, dst, off, None)) => {
// assert!(size <= 8);
// let off_rem = 8 * (off % 8);
// let freg = dst.get() + (off / 8) as u8;
// if size < 8 {
// let mask = !(((1u64 << (8 * size)) - 1) << off_rem);
// self.ci.emit(andi(freg, freg, mask));
// if off_rem == 0 {
// self.ci.emit(or(freg, freg, src.get()));
// } else {
// let tmp = self.ci.regs.allocate();
// self.ci.emit(slui64(tmp.get(), src.get(), off_rem as _));
// self.ci.emit(or(freg, freg, src.get()));
// self.ci.regs.free(tmp);
// }
// } else {
// self.ci.emit(cp(freg, src.get()));
// }
//}
(lpat!(true, src, soff, ref ssta), lpat!(false, dst, 0, None)) => { (lpat!(true, src, soff, ref ssta), lpat!(false, dst, 0, None)) => {
if size < 8 { if size < 8 {
self.ci.emit(cp(dst.get(), 0)); self.ci.emit(cp(dst.get(), 0));
@ -2740,5 +2781,6 @@ mod tests {
writing_into_string => README; writing_into_string => README;
request_page => README; request_page => README;
tests_ptr_to_ptr_copy => README; tests_ptr_to_ptr_copy => README;
wide_ret => README;
} }
} }

View file

@ -371,6 +371,10 @@ impl<'a> Lexer<'a> {
Self { pos, bytes: input.as_bytes() } Self { pos, bytes: input.as_bytes() }
} }
pub fn source(&self) -> &'a str {
unsafe { std::str::from_utf8_unchecked(self.bytes) }
}
pub fn slice(&self, tok: std::ops::Range<usize>) -> &'a str { pub fn slice(&self, tok: std::ops::Range<usize>) -> &'a str {
unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) } unsafe { std::str::from_utf8_unchecked(&self.bytes[tok]) }
} }
@ -511,10 +515,6 @@ impl<'a> Lexer<'a> {
false false
} }
} }
pub fn line_col(&self, pos: u32) -> (usize, usize) {
line_col(self.bytes, pos)
}
} }
pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) { pub fn line_col(bytes: &[u8], pos: u32) -> (usize, usize) {

View file

@ -27,10 +27,11 @@ use {
son::reg, son::reg,
ty::ArrayLen, ty::ArrayLen,
}, },
hbbytecode as instrs,
parser::Ast, parser::Ast,
std::{ std::{
collections::{hash_map, BTreeMap, VecDeque}, collections::{hash_map, BTreeMap, VecDeque},
io::{self, Read}, io,
ops::Range, ops::Range,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc, rc::Rc,
@ -52,7 +53,6 @@ pub mod codegen;
pub mod parser; pub mod parser;
pub mod son; pub mod son;
mod instrs;
mod lexer; mod lexer;
mod task { mod task {
@ -601,6 +601,19 @@ impl ParamAlloc {
} }
} }
#[repr(packed)]
#[allow(dead_code)]
struct AbleOsExecutableHeader {
magic_number: [u8; 3],
executable_version: u32,
code_length: u64,
data_length: u64,
debug_length: u64,
config_length: u64,
metadata_length: u64,
}
#[derive(Default)] #[derive(Default)]
struct Types { struct Types {
syms: HashMap<SymKey, ty::Id>, syms: HashMap<SymKey, ty::Id>,
@ -613,15 +626,24 @@ struct Types {
arrays: Vec<Array>, arrays: Vec<Array>,
} }
const HEADER_SIZE: usize = std::mem::size_of::<AbleOsExecutableHeader>();
impl Types { impl Types {
fn assemble(&mut self, to: &mut Vec<u8>) { fn assemble(&mut self, to: &mut Vec<u8>) {
to.extend([0u8; HEADER_SIZE]);
emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0)); emit(to, instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
emit(to, instrs::tx()); emit(to, instrs::tx());
self.dump_reachable(0, to); let exe = self.dump_reachable(0, to);
Reloc::new(0, 3, 4).apply_jump(to, self.funcs[0].offset, 0); Reloc::new(HEADER_SIZE, 3, 4).apply_jump(to, self.funcs[0].offset, 0);
unsafe { *to.as_mut_ptr().cast::<AbleOsExecutableHeader>() = exe }
} }
fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) { fn dump_reachable(&mut self, from: ty::Func, to: &mut Vec<u8>) -> AbleOsExecutableHeader {
let mut used_funcs = vec![];
let mut used_globals = vec![];
let mut frontier = vec![ty::Kind::Func(from).compress()]; let mut frontier = vec![ty::Kind::Func(from).compress()];
while let Some(itm) = frontier.pop() { while let Some(itm) = frontier.pop() {
@ -631,8 +653,8 @@ impl Types {
if task::is_done(fuc.offset) { if task::is_done(fuc.offset) {
continue; continue;
} }
fuc.offset = to.len() as _; fuc.offset = 0;
to.extend(&fuc.code); used_funcs.push(func);
frontier.extend(fuc.relocs.iter().map(|r| r.target)); frontier.extend(fuc.relocs.iter().map(|r| r.target));
} }
ty::Kind::Global(glob) => { ty::Kind::Global(glob) => {
@ -640,18 +662,31 @@ impl Types {
if task::is_done(glb.offset) { if task::is_done(glb.offset) {
continue; continue;
} }
glb.offset = to.len() as _; glb.offset = 0;
to.extend(&glb.data); used_globals.push(glob);
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
for fuc in &self.funcs { for &func in &used_funcs {
if !task::is_done(fuc.offset) { let fuc = &mut self.funcs[func as usize];
continue; fuc.offset = to.len() as _;
to.extend(&fuc.code);
} }
let code_length = to.len();
for &global in &used_globals {
let global = &mut self.globals[global as usize];
global.offset = to.len() as _;
to.extend(&global.data);
}
let data_length = to.len() - code_length;
for func in used_funcs {
let fuc = &self.funcs[func as usize];
for rel in &fuc.relocs { for rel in &fuc.relocs {
let offset = match rel.target.expand() { let offset = match rel.target.expand() {
ty::Kind::Func(fun) => self.funcs[fun as usize].offset, ty::Kind::Func(fun) => self.funcs[fun as usize].offset,
@ -661,6 +696,16 @@ impl Types {
rel.reloc.apply_jump(to, offset, fuc.offset); rel.reloc.apply_jump(to, offset, fuc.offset);
} }
} }
AbleOsExecutableHeader {
magic_number: [0x15, 0x91, 0xD2],
executable_version: 0,
code_length: (code_length - HEADER_SIZE) as _,
data_length: data_length as _,
debug_length: 0,
config_length: 0,
metadata_length: 0,
}
} }
pub fn disasm( pub fn disasm(
@ -670,7 +715,7 @@ impl Types {
output: &mut impl std::io::Write, output: &mut impl std::io::Write,
eca_handler: impl FnMut(&mut &[u8]), eca_handler: impl FnMut(&mut &[u8]),
) -> std::io::Result<()> { ) -> std::io::Result<()> {
use crate::DisasmItem; use instrs::DisasmItem;
let functions = self let functions = self
.funcs .funcs
.iter() .iter()
@ -699,7 +744,7 @@ impl Types {
(g.offset, (name, g.data.len() as Size, DisasmItem::Global)) (g.offset, (name, g.data.len() as Size, DisasmItem::Global))
})) }))
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
crate::disasm(&mut sluce, &functions, output, eca_handler) instrs::disasm(&mut sluce, &functions, output, eca_handler)
} }
fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc { fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc {
@ -812,178 +857,6 @@ impl Types {
} }
} }
#[inline]
unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
let mut buf = [0; instrs::MAX_SIZE];
std::ptr::write(buf.as_mut_ptr() as *mut T, instr);
(std::mem::size_of::<T>(), buf)
}
#[inline]
fn decode<T>(binary: &mut &[u8]) -> Option<T> {
unsafe { Some(std::ptr::read(binary.take(..std::mem::size_of::<T>())?.as_ptr() as *const T)) }
}
#[derive(Clone, Copy)]
enum DisasmItem {
Func,
Global,
}
fn disasm(
binary: &mut &[u8],
functions: &BTreeMap<u32, (&str, u32, DisasmItem)>,
out: &mut impl std::io::Write,
mut eca_handler: impl FnMut(&mut &[u8]),
) -> std::io::Result<()> {
use self::instrs::Instr;
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 = 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() {
if matches!(kind, DisasmItem::Global) {
continue;
}
let prev = *binary;
binary.take(..off as usize).unwrap();
let mut label_count = 0;
while let Some(&byte) = binary.first() {
let offset: i32 = (prev.len() - binary.len()).try_into().unwrap();
if offset as u32 == off + len {
break;
}
let Ok(inst) = instr_from_byte(byte) else { break };
instrs::parse_args(binary, inst, &mut buf).ok_or(std::io::ErrorKind::OutOfMemory)?;
for op in buf.drain(..) {
let rel = match op {
instrs::Oper::O(rel) => rel,
instrs::Oper::P(rel) => rel.into(),
_ => continue,
};
has_cycle |= rel == 0;
let global_offset: u32 = (offset + rel).try_into().unwrap();
if functions.get(&global_offset).is_some() {
continue;
}
label_count += labels.try_insert(global_offset, label_count).is_ok() as u32;
}
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
}
*binary = prev;
}
'_dump: for (&off, &(name, len, kind)) in functions.iter() {
if matches!(kind, DisasmItem::Global) {
continue;
}
let prev = *binary;
writeln!(out, "{name}:")?;
binary.take(..off as usize).unwrap();
while let Some(&byte) = binary.first() {
let offset: i32 = (prev.len() - binary.len()).try_into().unwrap();
if offset as u32 == off + len {
break;
}
let Ok(inst) = instr_from_byte(byte) else {
writeln!(out, "invalid instr {byte}")?;
break;
};
instrs::parse_args(binary, inst, &mut buf).unwrap();
if let Some(label) = labels.get(&offset.try_into().unwrap()) {
write!(out, "{:>2}: ", label)?;
} else {
write!(out, " ")?;
}
write!(out, "{inst:<8?} ")?;
'a: for (i, op) in buf.drain(..).enumerate() {
if i != 0 {
write!(out, ", ")?;
}
let rel = 'b: {
match op {
instrs::Oper::O(rel) => break 'b rel,
instrs::Oper::P(rel) => break 'b rel.into(),
instrs::Oper::R(r) => write!(out, "r{r}")?,
instrs::Oper::B(b) => write!(out, "{b}b")?,
instrs::Oper::H(h) => write!(out, "{h}h")?,
instrs::Oper::W(w) => write!(out, "{w}w")?,
instrs::Oper::D(d) if (d as i64) < 0 => write!(out, "{}d", d as i64)?,
instrs::Oper::D(d) => write!(out, "{d}d")?,
instrs::Oper::A(a) => write!(out, "{a}a")?,
}
continue 'a;
};
let global_offset: u32 = (offset + rel).try_into().unwrap();
if let Some(&(name, ..)) = functions.get(&global_offset) {
if name.contains('\0') {
write!(out, ":{name:?}")?;
} else {
write!(out, ":{name}")?;
}
} else {
let local_has_oob = global_offset < off
|| global_offset > off + len
|| 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 {
write!(out, "!!!!!!!!!{rel}")?;
} else {
write!(out, ":{label}")?;
}
}
}
writeln!(out)?;
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
}
*binary = prev;
}
if has_oob {
return Err(std::io::ErrorKind::InvalidInput.into());
}
if has_cycle {
return Err(std::io::ErrorKind::TimedOut.into());
}
Ok(())
}
struct TaskQueue<T> { struct TaskQueue<T> {
inner: Mutex<TaskQueueInner<T>>, inner: Mutex<TaskQueueInner<T>>,
} }
@ -1070,107 +943,24 @@ impl<T> TaskQueueInner<T> {
} }
pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> { pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
const GIT_DEPS_DIR: &str = "git-deps"; fn resolve(path: &str, from: &str) -> Result<PathBuf, CantLoadFile> {
let path = match Path::new(from).parent() {
enum Chk<'a> {
Branch(&'a str),
Rev(&'a str),
Tag(&'a str),
}
enum ImportPath<'a> {
Rel { path: &'a str },
Git { link: &'a str, path: &'a str, chk: Option<Chk<'a>> },
}
impl<'a> TryFrom<&'a str> for ImportPath<'a> {
type Error = ParseImportError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let (prefix, path) = value.split_once(':').unwrap_or(("", value));
match prefix {
"rel" | "" => Ok(Self::Rel { path }),
"git" => {
let (link, path) =
path.split_once(':').ok_or(ParseImportError::ExpectedPath)?;
let (link, params) = link.split_once('?').unwrap_or((link, ""));
let chk = params.split('&').filter_map(|s| s.split_once('=')).find_map(
|(key, value)| match key {
"branch" => Some(Chk::Branch(value)),
"rev" => Some(Chk::Rev(value)),
"tag" => Some(Chk::Tag(value)),
_ => None,
},
);
Ok(Self::Git { link, path, chk })
}
_ => Err(ParseImportError::InvalidPrefix),
}
}
}
fn preprocess_git(link: &str) -> &str {
let link = link.strip_prefix("https://").unwrap_or(link);
link.strip_suffix(".git").unwrap_or(link)
}
impl<'a> ImportPath<'a> {
fn resolve(&self, from: &str) -> Result<PathBuf, CantLoadFile> {
let path = match self {
Self::Rel { path } => match Path::new(from).parent() {
Some(parent) => PathBuf::from_iter([parent, Path::new(path)]), Some(parent) => PathBuf::from_iter([parent, Path::new(path)]),
None => PathBuf::from(path), None => PathBuf::from(path),
},
Self::Git { path, link, .. } => {
let link = preprocess_git(link);
PathBuf::from_iter([GIT_DEPS_DIR, link, path])
}
}; };
path.canonicalize().map_err(|source| CantLoadFile { path.canonicalize().map_err(|source| CantLoadFile { path, source })
path,
from: PathBuf::from(from),
source,
})
}
}
#[derive(Debug)]
enum ParseImportError {
ExpectedPath,
InvalidPrefix,
}
impl std::fmt::Display for ParseImportError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ExpectedPath => "expected path".fmt(f),
Self::InvalidPrefix => "invalid prefix, expected one of rel, \
git or none followed by colon"
.fmt(f),
}
}
}
impl std::error::Error for ParseImportError {}
impl From<ParseImportError> for io::Error {
fn from(e: ParseImportError) -> Self {
io::Error::new(io::ErrorKind::InvalidInput, e)
}
} }
#[derive(Debug)] #[derive(Debug)]
struct CantLoadFile { struct CantLoadFile {
path: PathBuf, path: PathBuf,
from: PathBuf,
source: io::Error, source: io::Error,
} }
impl std::fmt::Display for CantLoadFile { impl std::fmt::Display for CantLoadFile {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "can't load file: {} (from: {})", self.path.display(), self.from.display(),) write!(f, "can't load file: {}", parser::display_rel_path(&self.path),)
} }
} }
@ -1186,37 +976,22 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
} }
} }
#[derive(Debug)] type Task = (u32, PathBuf);
struct InvalidFileData(std::str::Utf8Error);
impl std::fmt::Display for InvalidFileData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "invalid file data")
}
}
impl std::error::Error for InvalidFileData {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.0)
}
}
impl From<InvalidFileData> for io::Error {
fn from(e: InvalidFileData) -> Self {
io::Error::new(io::ErrorKind::InvalidData, e)
}
}
type Task = (u32, PathBuf, Option<std::process::Command>);
let seen = Mutex::new(HashMap::<PathBuf, u32>::default()); let seen = Mutex::new(HashMap::<PathBuf, u32>::default());
let tasks = TaskQueue::<Task>::new(extra_threads + 1); let tasks = TaskQueue::<Task>::new(extra_threads + 1);
let ast = Mutex::new(Vec::<io::Result<Ast>>::new()); let ast = Mutex::new(Vec::<io::Result<Ast>>::new());
let loader = |path: &str, from: &str| { let loader = |path: &str, from: &str| {
let path = ImportPath::try_from(path)?; if path.starts_with("rel:") {
return Err(io::Error::new(
io::ErrorKind::Other,
"`rel:` prefix was removed and is now equivalent to no prefix (remove it)"
.to_string(),
));
}
let physiscal_path = path.resolve(from)?; let physiscal_path = resolve(path, from)?;
let id = { let id = {
let mut seen = seen.lock().unwrap(); let mut seen = seen.lock().unwrap();
@ -1232,63 +1007,30 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
} }
}; };
let command = if !physiscal_path.exists() { if !physiscal_path.exists() {
let ImportPath::Git { link, chk, .. } = path else {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::NotFound, io::ErrorKind::NotFound,
format!("can't find file: {}", physiscal_path.display()), format!("can't find file: {}", parser::display_rel_path(&physiscal_path)),
)); ));
};
let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]);
let mut command = std::process::Command::new("git");
command.args(["clone", "--depth", "1"]);
if let Some(chk) = chk {
command.args(match chk {
Chk::Branch(b) => ["--branch", b],
Chk::Tag(t) => ["--tag", t],
Chk::Rev(r) => ["--rev", r],
});
} }
command.arg(link).arg(root);
Some(command)
} else {
None
};
tasks.push((id, physiscal_path, command)); tasks.push((id, physiscal_path));
Ok(id) Ok(id)
}; };
let execute_task = |(_, path, command): Task, buffer: &mut Vec<u8>| { let execute_task = |(_, path): Task| {
if let Some(mut command) = command {
let output = command.output()?;
if !output.status.success() {
let msg =
format!("git command failed: {}", String::from_utf8_lossy(&output.stderr));
return Err(io::Error::new(io::ErrorKind::Other, msg));
}
}
let path = path.to_str().ok_or_else(|| { let path = path.to_str().ok_or_else(|| {
io::Error::new( io::Error::new(
io::ErrorKind::InvalidData, io::ErrorKind::InvalidData,
format!("path contains invalid characters: {}", path.display()), format!("path contains invalid characters: {}", parser::display_rel_path(&path)),
) )
})?; })?;
let mut file = std::fs::File::open(path)?; Ok(Ast::new(path, std::fs::read_to_string(path)?, &loader))
file.read_to_end(buffer)?;
let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?;
Ok(Ast::new(path, src.to_owned(), &loader))
}; };
let thread = || { let thread = || {
let mut buffer = Vec::new();
while let Some(task @ (indx, ..)) = tasks.pop() { while let Some(task @ (indx, ..)) = tasks.pop() {
let res = execute_task(task, &mut buffer); let res = execute_task(task);
buffer.clear();
let mut ast = ast.lock().unwrap(); let mut ast = ast.lock().unwrap();
let len = ast.len().max(indx as usize + 1); let len = ast.len().max(indx as usize + 1);
ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into())); ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into()));
@ -1298,7 +1040,7 @@ pub fn parse_from_fs(extra_threads: usize, root: &str) -> io::Result<Vec<Ast>> {
let path = Path::new(root).canonicalize()?; let path = Path::new(root).canonicalize()?;
seen.lock().unwrap().insert(path.clone(), 0); seen.lock().unwrap().insert(path.clone(), 0);
tasks.push((0, path, None)); tasks.push((0, path));
if extra_threads == 0 { if extra_threads == 0 {
thread(); thread();
@ -1467,7 +1209,7 @@ fn test_run_vm(out: &[u8], output: &mut String) {
let mut vm = unsafe { let mut vm = unsafe {
hbvm::Vm::<_, { 1024 * 100 }>::new( hbvm::Vm::<_, { 1024 * 100 }>::new(
LoggedMem::default(), LoggedMem::default(),
hbvm::mem::Address::new(out.as_ptr() as u64), hbvm::mem::Address::new(out.as_ptr() as u64).wrapping_add(HEADER_SIZE),
) )
}; };
@ -1506,7 +1248,7 @@ fn test_run_vm(out: &[u8], output: &mut String) {
} }
}; };
writeln!(output, "code size: {}", out.len()).unwrap(); writeln!(output, "code size: {}", out.len() - HEADER_SIZE).unwrap();
writeln!(output, "ret: {:?}", vm.read_reg(1).0).unwrap(); writeln!(output, "ret: {:?}", vm.read_reg(1).0).unwrap();
writeln!(output, "status: {:?}", stat).unwrap(); writeln!(output, "status: {:?}", stat).unwrap();
} }

View file

@ -5,8 +5,10 @@ use {
}, },
std::{ std::{
cell::{Cell, UnsafeCell}, cell::{Cell, UnsafeCell},
ffi::OsStr,
fmt, io, fmt, io,
ops::{Deref, Not}, ops::{Deref, Not},
path::PathBuf,
ptr::NonNull, ptr::NonNull,
sync::atomic::AtomicUsize, sync::atomic::AtomicUsize,
}, },
@ -95,20 +97,20 @@ impl<'a, 'b> Parser<'a, 'b> {
let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true)); let f = self.collect_list(TokenKind::Semi, TokenKind::Eof, |s| s.expr_low(true));
self.pop_scope(0); self.pop_scope(0);
let has_undeclared = !self.idents.is_empty(); let mut errors = String::new();
for id in self.idents.drain(..) { for id in self.idents.drain(..) {
let (line, col) = self.lexer.line_col(ident::pos(id.ident)); report_to(
eprintln!( self.lexer.source(),
"{}:{}:{} => undeclared identifier: {}",
self.path, self.path,
line, id.ident,
col, format_args!("undeclared identifier: {}", self.lexer.slice(ident::range(id.ident))),
self.lexer.slice(ident::range(id.ident)) &mut errors,
); );
} }
if has_undeclared { if !errors.is_empty() {
// TODO: we need error recovery // TODO: we need error recovery
eprintln!("{errors}");
unreachable!(); unreachable!();
} }
@ -193,7 +195,7 @@ impl<'a, 'b> Parser<'a, 'b> {
self.declare_rec(value, top_level) self.declare_rec(value, top_level)
} }
} }
_ => self.report_pos(expr.pos(), "cant declare this shit (yet)"), _ => self.report(expr.pos(), "cant declare this shit (yet)"),
} }
} }
@ -201,7 +203,7 @@ impl<'a, 'b> Parser<'a, 'b> {
if let Some(index) = index_to_check if let Some(index) = index_to_check
&& index != 0 && index != 0
{ {
self.report_pos( self.report(
pos, pos,
format_args!( format_args!(
"out of order declaration not allowed: {}", "out of order declaration not allowed: {}",
@ -212,7 +214,7 @@ impl<'a, 'b> Parser<'a, 'b> {
let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up"); let index = self.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up");
if std::mem::replace(&mut self.idents[index].declared, true) { if std::mem::replace(&mut self.idents[index].declared, true) {
self.report_pos( self.report(
pos, pos,
format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))), format_args!("redeclaration of identifier: {}", self.lexer.slice(ident::range(id))),
) )
@ -276,7 +278,9 @@ impl<'a, 'b> Parser<'a, 'b> {
path: self.arena.alloc_str(path), path: self.arena.alloc_str(path),
id: match (self.loader)(path, self.path) { id: match (self.loader)(path, self.path) {
Ok(id) => id, Ok(id) => id,
Err(e) => self.report(format_args!("error loading dependency: {e:#}")), Err(e) => {
self.report(str.start, format_args!("error loading dependency: {e:#}"))
}
}, },
} }
} }
@ -408,7 +412,7 @@ impl<'a, 'b> Parser<'a, 'b> {
pos, pos,
value: match u64::from_str_radix(slice, radix as u32) { value: match u64::from_str_radix(slice, radix as u32) {
Ok(value) => value, Ok(value) => value,
Err(e) => self.report(format_args!("invalid number: {e}")), Err(e) => self.report(token.start, format_args!("invalid number: {e}")),
} as i64, } as i64,
radix, radix,
} }
@ -419,7 +423,7 @@ impl<'a, 'b> Parser<'a, 'b> {
expr expr
} }
T::Comment => Expr::Comment { pos, literal: self.move_str(token) }, T::Comment => Expr::Comment { pos, literal: self.move_str(token) },
tok => self.report(format_args!("unexpected token: {tok:?}")), tok => self.report(token.start, format_args!("unexpected token: {tok:?}")),
}; };
loop { loop {
@ -497,7 +501,10 @@ impl<'a, 'b> Parser<'a, 'b> {
if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) { if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) {
self.next() self.next()
} else { } else {
self.report(format_args!("expected identifier, found {:?}", self.token.kind)) self.report(
self.token.start,
format_args!("expected identifier, found {:?}", self.token.kind),
)
} }
} }
@ -552,20 +559,19 @@ impl<'a, 'b> Parser<'a, 'b> {
fn expect_advance(&mut self, kind: TokenKind) -> Token { fn expect_advance(&mut self, kind: TokenKind) -> Token {
if self.token.kind != kind { if self.token.kind != kind {
self.report(format_args!("expected {:?}, found {:?}", kind, self.token.kind)); self.report(
self.token.start,
format_args!("expected {:?}, found {:?}", kind, self.token.kind),
);
} }
self.next() self.next()
} }
#[track_caller] #[track_caller]
fn report(&self, msg: impl fmt::Display) -> ! { fn report(&self, pos: Pos, msg: impl fmt::Display) -> ! {
self.report_pos(self.token.start, msg) let mut str = String::new();
} report_to(self.lexer.source(), self.path, pos, msg, &mut str);
eprintln!("{str}");
#[track_caller]
fn report_pos(&self, pos: Pos, msg: impl fmt::Display) -> ! {
let (line, col) = self.lexer.line_col(pos);
eprintln!("{}:{}:{} => {}", self.path, line, col, msg);
unreachable!(); unreachable!();
} }
@ -1231,17 +1237,32 @@ impl AstInner<[Symbol]> {
} }
pub fn report_to(&self, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) { pub fn report_to(&self, pos: Pos, msg: impl fmt::Display, out: &mut impl fmt::Write) {
let str = &self.file; report_to(&self.file, &self.path, pos, msg, out);
let (line, mut col) = lexer::line_col(str.as_bytes(), pos); }
_ = writeln!(out, "{}:{}:{}: {}", self.path, line, col, msg); }
let line = &str[str[..pos as usize].rfind('\n').map_or(0, |i| i + 1) pub fn display_rel_path(path: &(impl AsRef<OsStr> + ?Sized)) -> std::path::Display {
..str[pos as usize..].find('\n').unwrap_or(str.len()) + pos as usize]; static CWD: std::sync::LazyLock<PathBuf> =
std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default());
std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display()
}
pub fn report_to(
file: &str,
path: &str,
pos: Pos,
msg: impl fmt::Display,
out: &mut impl fmt::Write,
) {
let (line, mut col) = lexer::line_col(file.as_bytes(), pos);
_ = writeln!(out, "{}:{}:{}: {}", display_rel_path(path), line, col, msg);
let line = &file[file[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
..file[pos as usize..].find('\n').unwrap_or(file.len()) + pos as usize];
col += line.matches('\t').count() * 3; col += line.matches('\t').count() * 3;
_ = writeln!(out, "{}", line.replace("\t", " ")); _ = writeln!(out, "{}", line.replace("\t", " "));
_ = writeln!(out, "{}^", " ".repeat(col - 1)); _ = writeln!(out, "{}^", " ".repeat(col - 1));
}
} }
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,9 @@
foo:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
main: main:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h ST r31, r254, 0a, 8h
@ -6,12 +12,6 @@ main:
LD r31, r254, 0a, 8h LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d ADDI64 r254, r254, 8d
JALA r0, r31, 0a 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: 143 code size: 143
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,19 @@
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: main:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
@ -11,22 +27,6 @@ main:
LD r31, r254, 0a, 24h LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d ADDI64 r254, r254, 24d
JALA r0, r31, 0a 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
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
code size: 257 code size: 257
ret: 33 ret: 33
status: Ok(()) status: Ok(())

View file

@ -1,20 +1,3 @@
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
add: add:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
@ -33,6 +16,23 @@ add:
LD r31, r254, 0a, 24h LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d ADDI64 r254, r254, 24d
JALA r0, r31, 0a 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 code size: 275
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,33 @@
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: main:
ADDI64 r254, r254, -48d ADDI64 r254, r254, -48d
ST r31, r254, 24a, 24h ST r31, r254, 24a, 24h
@ -17,20 +47,18 @@ main:
LD r31, r254, 24a, 24h LD r31, r254, 24a, 24h
ADDI64 r254, r254, 48d ADDI64 r254, r254, 48d
JALA r0, r31, 0a JALA r0, r31, 0a
deinit: malloc:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 32h
CP r32, r2 CP r32, r2
LD r2, r32, 0a, 8h CP r33, r3
LD r33, r32, 16a, 8h LRA r34, r0, :MALLOC_SYS_CALL
MULI64 r33, r33, 8d LD r2, r34, 0a, 8h
CP r3, r33 CP r3, r32
LI64 r4, 8d CP r4, r33
JAL r31, r0, :free ECA
CP r1, r32 LD r31, r254, 0a, 32h
JAL r31, r0, :new ADDI64 r254, r254, 32d
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a JALA r0, r31, 0a
new: new:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
@ -45,21 +73,6 @@ new:
LD r31, r254, 0a, 24h LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d ADDI64 r254, r254, 24d
JALA r0, r31, 0a 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
push: push:
ADDI64 r254, r254, -72d ADDI64 r254, r254, -72d
ST r31, r254, 0a, 72h ST r31, r254, 0a, 72h
@ -124,19 +137,6 @@ push:
4: LD r31, r254, 0a, 72h 4: LD r31, r254, 0a, 72h
ADDI64 r254, r254, 72d ADDI64 r254, r254, 72d
JALA r0, r31, 0a 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
code size: 1201 code size: 1201
ret: 69 ret: 69
status: Ok(()) status: Ok(())

View file

@ -1,11 +1,3 @@
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: fib:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
@ -27,6 +19,14 @@ fib:
1: LD r31, r254, 0a, 24h 1: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d ADDI64 r254, r254, 24d
JALA r0, r31, 0a 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 code size: 231
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,45 +1,3 @@
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
example: example:
ADDI64 r254, r254, -48d ADDI64 r254, r254, -48d
ST r31, r254, 0a, 48h ST r31, r254, 0a, 48h
@ -103,6 +61,69 @@ integer:
1: LD r31, r254, 0a, 56h 1: LD r31, r254, 0a, 56h
ADDI64 r254, r254, 56d ADDI64 r254, r254, 56d
JALA r0, r31, 0a 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: rect_line:
ADDI64 r254, r254, -112d ADDI64 r254, r254, -112d
ST r31, r254, 48a, 64h ST r31, r254, 48a, 64h
@ -135,27 +156,6 @@ rect_line:
1: LD r31, r254, 48a, 64h 1: LD r31, r254, 48a, 64h
ADDI64 r254, r254, 112d ADDI64 r254, r254, 112d
JALA r0, r31, 0a 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
code size: 1400 code size: 1400
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,13 +1,3 @@
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
integer_range: integer_range:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h
@ -24,6 +14,16 @@ integer_range:
LD r31, r254, 0a, 32h LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a 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 code size: 210
ret: 42 ret: 42
status: Ok(()) status: Ok(())

View file

@ -1,11 +1,3 @@
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: fib:
ADDI64 r254, r254, -40d ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h ST r31, r254, 0a, 40h
@ -25,6 +17,14 @@ fib:
LD r31, r254, 0a, 40h LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d ADDI64 r254, r254, 40d
JALA r0, r31, 0a 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 code size: 218
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,10 @@
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: main:
ADDI64 r254, r254, -40d ADDI64 r254, r254, -40d
ST r31, r254, 8a, 32h ST r31, r254, 8a, 32h
@ -14,13 +21,6 @@ main:
LD r31, r254, 8a, 32h LD r31, r254, 8a, 32h
ADDI64 r254, r254, 40d ADDI64 r254, r254, 40d
JALA r0, r31, 0a JALA r0, r31, 0a
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
modify: modify:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h

View file

@ -1,12 +1,3 @@
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
create_back_buffer: create_back_buffer:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h
@ -38,6 +29,15 @@ create_back_buffer:
1: LD r31, r254, 0a, 32h 1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a 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: request_page:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h

View file

@ -1,43 +1,3 @@
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
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
fib: fib:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h
@ -60,6 +20,46 @@ fib:
1: LD r31, r254, 0a, 32h 1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a 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 code size: 452
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,16 @@
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: main:
ADDI64 r254, r254, -40d ADDI64 r254, r254, -40d
ST r31, r254, 16a, 24h ST r31, r254, 16a, 24h
@ -21,17 +34,6 @@ main:
LD r31, r254, 16a, 24h LD r31, r254, 16a, 24h
ADDI64 r254, r254, 40d ADDI64 r254, r254, 40d
JALA r0, r31, 0a JALA r0, r31, 0a
foo: code size: 341
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r1, 3d
ANDI r2, r2, -4294967296d
ORI r2, r2, 2d
ANDI r2, r2, 4294967295d
ORI r2, r2, 8589934592d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 313
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -21,16 +21,6 @@ main:
1: LD r31, r254, 24a, 24h 1: LD r31, r254, 24a, 24h
ADDI64 r254, r254, 48d ADDI64 r254, r254, 48d
JALA r0, r31, 0a 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
odher_pass: odher_pass:
ADDI64 r254, r254, -32d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h
@ -41,6 +31,16 @@ odher_pass:
LD r31, r254, 0a, 32h LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a 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 code size: 394
ret: 3 ret: 3
status: Ok(()) status: Ok(())

View 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(())

View file

@ -1,3 +1,10 @@
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: main:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h ST r31, r254, 0a, 8h
@ -6,13 +13,6 @@ main:
LD r31, r254, 0a, 8h LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d ADDI64 r254, r254, 8d
JALA r0, r31, 0a JALA r0, r31, 0a
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
outl: outl:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h ST r31, r254, 0a, 16h

View file

@ -9,4 +9,4 @@ alloc = []
nightly = [] nightly = []
[dependencies] [dependencies]
hbbytecode = { path = "../hbbytecode" } hbbytecode = { path = "../hbbytecode", default-features = false }

View file

@ -34,7 +34,7 @@ where
/// Program can return [`VmRunError`] if a trap handling failed /// Program can return [`VmRunError`] if a trap handling failed
#[cfg_attr(feature = "nightly", repr(align(4096)))] #[cfg_attr(feature = "nightly", repr(align(4096)))]
pub fn run(&mut self) -> Result<VmRunOk, VmRunError> { pub fn run(&mut self) -> Result<VmRunOk, VmRunError> {
use hbbytecode::opcode::*; use hbbytecode::Instr as I;
loop { loop {
// Big match // Big match
// //
@ -56,105 +56,112 @@ where
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU // - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
// sorry 8 bit fans, HBVM won't run on your Speccy :( // sorry 8 bit fans, HBVM won't run on your Speccy :(
unsafe { unsafe {
match self.memory.prog_read::<u8>(self.pc as _) { match self
UN => { .memory
.prog_read::<u8>(self.pc as _)
.try_into()
.map_err(VmRunError::InvalidOpcode)?
{
I::UN => {
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Err(VmRunError::Unreachable); return Err(VmRunError::Unreachable);
} }
TX => { I::TX => {
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Ok(VmRunOk::End); return Ok(VmRunOk::End);
} }
NOP => handler!(self, |OpsN()| ()), I::NOP => handler!(self, |OpsN()| ()),
ADD8 => self.binary_op(u8::wrapping_add), I::ADD8 => self.binary_op(u8::wrapping_add),
ADD16 => self.binary_op(u16::wrapping_add), I::ADD16 => self.binary_op(u16::wrapping_add),
ADD32 => self.binary_op(u32::wrapping_add), I::ADD32 => self.binary_op(u32::wrapping_add),
ADD64 => self.binary_op(u64::wrapping_add), I::ADD64 => self.binary_op(u64::wrapping_add),
SUB8 => self.binary_op(u8::wrapping_sub), I::SUB8 => self.binary_op(u8::wrapping_sub),
SUB16 => self.binary_op(u16::wrapping_sub), I::SUB16 => self.binary_op(u16::wrapping_sub),
SUB32 => self.binary_op(u32::wrapping_sub), I::SUB32 => self.binary_op(u32::wrapping_sub),
SUB64 => self.binary_op(u64::wrapping_sub), I::SUB64 => self.binary_op(u64::wrapping_sub),
MUL8 => self.binary_op(u8::wrapping_mul), I::MUL8 => self.binary_op(u8::wrapping_mul),
MUL16 => self.binary_op(u16::wrapping_mul), I::MUL16 => self.binary_op(u16::wrapping_mul),
MUL32 => self.binary_op(u32::wrapping_mul), I::MUL32 => self.binary_op(u32::wrapping_mul),
MUL64 => self.binary_op(u64::wrapping_mul), I::MUL64 => self.binary_op(u64::wrapping_mul),
AND => self.binary_op::<u64>(ops::BitAnd::bitand), I::AND => self.binary_op::<u64>(ops::BitAnd::bitand),
OR => self.binary_op::<u64>(ops::BitOr::bitor), I::OR => self.binary_op::<u64>(ops::BitOr::bitor),
XOR => self.binary_op::<u64>(ops::BitXor::bitxor), I::XOR => self.binary_op::<u64>(ops::BitXor::bitxor),
SLU8 => self.binary_op_shift::<u8>(u8::wrapping_shl), I::SLU8 => self.binary_op_shift::<u8>(u8::wrapping_shl),
SLU16 => self.binary_op_shift::<u16>(u16::wrapping_shl), I::SLU16 => self.binary_op_shift::<u16>(u16::wrapping_shl),
SLU32 => self.binary_op_shift::<u32>(u32::wrapping_shl), I::SLU32 => self.binary_op_shift::<u32>(u32::wrapping_shl),
SLU64 => self.binary_op_shift::<u64>(u64::wrapping_shl), I::SLU64 => self.binary_op_shift::<u64>(u64::wrapping_shl),
SRU8 => self.binary_op_shift::<u8>(u8::wrapping_shr), I::SRU8 => self.binary_op_shift::<u8>(u8::wrapping_shr),
SRU16 => self.binary_op_shift::<u16>(u16::wrapping_shr), I::SRU16 => self.binary_op_shift::<u16>(u16::wrapping_shr),
SRU32 => self.binary_op_shift::<u32>(u32::wrapping_shr), I::SRU32 => self.binary_op_shift::<u32>(u32::wrapping_shr),
SRU64 => self.binary_op_shift::<u64>(u64::wrapping_shr), I::SRU64 => self.binary_op_shift::<u64>(u64::wrapping_shr),
SRS8 => self.binary_op_shift::<i8>(i8::wrapping_shr), I::SRS8 => self.binary_op_shift::<i8>(i8::wrapping_shr),
SRS16 => self.binary_op_shift::<i16>(i16::wrapping_shr), I::SRS16 => self.binary_op_shift::<i16>(i16::wrapping_shr),
SRS32 => self.binary_op_shift::<i32>(i32::wrapping_shr), I::SRS32 => self.binary_op_shift::<i32>(i32::wrapping_shr),
SRS64 => self.binary_op_shift::<i64>(i64::wrapping_shr), I::SRS64 => self.binary_op_shift::<i64>(i64::wrapping_shr),
CMPU => handler!(self, |OpsRRR(tg, a0, a1)| self.cmp( I::CMPU => handler!(self, |OpsRRR(tg, a0, a1)| self.cmp(
tg, tg,
a0, a0,
self.read_reg(a1).cast::<u64>() self.read_reg(a1).cast::<u64>()
)), )),
CMPS => handler!(self, |OpsRRR(tg, a0, a1)| self.cmp( I::CMPS => handler!(self, |OpsRRR(tg, a0, a1)| self.cmp(
tg, tg,
a0, a0,
self.read_reg(a1).cast::<i64>() self.read_reg(a1).cast::<i64>()
)), )),
DIRU8 => self.dir::<u8>(), I::DIRU8 => self.dir::<u8>(),
DIRU16 => self.dir::<u16>(), I::DIRU16 => self.dir::<u16>(),
DIRU32 => self.dir::<u32>(), I::DIRU32 => self.dir::<u32>(),
DIRU64 => self.dir::<u64>(), I::DIRU64 => self.dir::<u64>(),
DIRS8 => self.dir::<i8>(), I::DIRS8 => self.dir::<i8>(),
DIRS16 => self.dir::<i16>(), I::DIRS16 => self.dir::<i16>(),
DIRS32 => self.dir::<i32>(), I::DIRS32 => self.dir::<i32>(),
DIRS64 => self.dir::<i64>(), I::DIRS64 => self.dir::<i64>(),
NEG => handler!(self, |OpsRR(tg, a0)| { I::NEG => handler!(self, |OpsRR(tg, a0)| {
// Bit negation // Bit negation
self.write_reg(tg, self.read_reg(a0).cast::<u64>().wrapping_neg()) self.write_reg(tg, self.read_reg(a0).cast::<u64>().wrapping_neg())
}), }),
NOT => handler!(self, |OpsRR(tg, a0)| { I::NOT => handler!(self, |OpsRR(tg, a0)| {
// Logical negation // Logical negation
self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0)); self.write_reg(tg, u64::from(self.read_reg(a0).cast::<u64>() == 0));
}), }),
SXT8 => handler!(self, |OpsRR(tg, a0)| { I::SXT8 => handler!(self, |OpsRR(tg, a0)| {
self.write_reg(tg, self.read_reg(a0).cast::<i8>() as i64) self.write_reg(tg, self.read_reg(a0).cast::<i8>() as i64)
}), }),
SXT16 => handler!(self, |OpsRR(tg, a0)| { I::SXT16 => handler!(self, |OpsRR(tg, a0)| {
self.write_reg(tg, self.read_reg(a0).cast::<i16>() as i64) self.write_reg(tg, self.read_reg(a0).cast::<i16>() as i64)
}), }),
SXT32 => handler!(self, |OpsRR(tg, a0)| { I::SXT32 => handler!(self, |OpsRR(tg, a0)| {
self.write_reg(tg, self.read_reg(a0).cast::<i32>() as i64) self.write_reg(tg, self.read_reg(a0).cast::<i32>() as i64)
}), }),
ADDI8 => self.binary_op_imm(u8::wrapping_add), I::ADDI8 => self.binary_op_imm(u8::wrapping_add),
ADDI16 => self.binary_op_imm(u16::wrapping_add), I::ADDI16 => self.binary_op_imm(u16::wrapping_add),
ADDI32 => self.binary_op_imm(u32::wrapping_add), I::ADDI32 => self.binary_op_imm(u32::wrapping_add),
ADDI64 => self.binary_op_imm(u64::wrapping_add), I::ADDI64 => self.binary_op_imm(u64::wrapping_add),
MULI8 => self.binary_op_imm(u8::wrapping_mul), I::MULI8 => self.binary_op_imm(u8::wrapping_mul),
MULI16 => self.binary_op_imm(u16::wrapping_mul), I::MULI16 => self.binary_op_imm(u16::wrapping_mul),
MULI32 => self.binary_op_imm(u32::wrapping_mul), I::MULI32 => self.binary_op_imm(u32::wrapping_mul),
MULI64 => self.binary_op_imm(u64::wrapping_mul), I::MULI64 => self.binary_op_imm(u64::wrapping_mul),
ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand), I::ANDI => self.binary_op_imm::<u64>(ops::BitAnd::bitand),
ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor), I::ORI => self.binary_op_imm::<u64>(ops::BitOr::bitor),
XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor), I::XORI => self.binary_op_imm::<u64>(ops::BitXor::bitxor),
SLUI8 => self.binary_op_ims::<u8>(u8::wrapping_shl), I::SLUI8 => self.binary_op_ims::<u8>(u8::wrapping_shl),
SLUI16 => self.binary_op_ims::<u16>(u16::wrapping_shl), I::SLUI16 => self.binary_op_ims::<u16>(u16::wrapping_shl),
SLUI32 => self.binary_op_ims::<u32>(u32::wrapping_shl), I::SLUI32 => self.binary_op_ims::<u32>(u32::wrapping_shl),
SLUI64 => self.binary_op_ims::<u64>(u64::wrapping_shl), I::SLUI64 => self.binary_op_ims::<u64>(u64::wrapping_shl),
SRUI8 => self.binary_op_ims::<u8>(u8::wrapping_shr), I::SRUI8 => self.binary_op_ims::<u8>(u8::wrapping_shr),
SRUI16 => self.binary_op_ims::<u16>(u16::wrapping_shr), I::SRUI16 => self.binary_op_ims::<u16>(u16::wrapping_shr),
SRUI32 => self.binary_op_ims::<u32>(u32::wrapping_shr), I::SRUI32 => self.binary_op_ims::<u32>(u32::wrapping_shr),
SRUI64 => self.binary_op_ims::<u64>(u64::wrapping_shr), I::SRUI64 => self.binary_op_ims::<u64>(u64::wrapping_shr),
SRSI8 => self.binary_op_ims::<i8>(i8::wrapping_shr), I::SRSI8 => self.binary_op_ims::<i8>(i8::wrapping_shr),
SRSI16 => self.binary_op_ims::<i16>(i16::wrapping_shr), I::SRSI16 => self.binary_op_ims::<i16>(i16::wrapping_shr),
SRSI32 => self.binary_op_ims::<i32>(i32::wrapping_shr), I::SRSI32 => self.binary_op_ims::<i32>(i32::wrapping_shr),
SRSI64 => self.binary_op_ims::<i64>(i64::wrapping_shr), I::SRSI64 => self.binary_op_ims::<i64>(i64::wrapping_shr),
CMPUI => handler!(self, |OpsRRD(tg, a0, imm)| { self.cmp(tg, a0, imm) }), I::CMPUI => handler!(self, |OpsRRD(tg, a0, imm)| { self.cmp(tg, a0, imm) }),
CMPSI => handler!(self, |OpsRRD(tg, a0, imm)| { self.cmp(tg, a0, imm as i64) }), I::CMPSI => {
CP => handler!(self, |OpsRR(tg, a0)| self.write_reg(tg, self.read_reg(a0))), handler!(self, |OpsRRD(tg, a0, imm)| { self.cmp(tg, a0, imm as i64) })
SWA => handler!(self, |OpsRR(r0, r1)| { }
I::CP => handler!(self, |OpsRR(tg, a0)| self.write_reg(tg, self.read_reg(a0))),
I::SWA => handler!(self, |OpsRR(r0, r1)| {
// Swap registers // Swap registers
match (r0, r1) { match (r0, r1) {
(0, 0) => (), (0, 0) => (),
@ -167,33 +174,33 @@ where
} }
} }
}), }),
LI8 => handler!(self, |OpsRB(tg, imm)| self.write_reg(tg, imm)), I::LI8 => handler!(self, |OpsRB(tg, imm)| self.write_reg(tg, imm)),
LI16 => handler!(self, |OpsRH(tg, imm)| self.write_reg(tg, imm)), I::LI16 => handler!(self, |OpsRH(tg, imm)| self.write_reg(tg, imm)),
LI32 => handler!(self, |OpsRW(tg, imm)| self.write_reg(tg, imm)), I::LI32 => handler!(self, |OpsRW(tg, imm)| self.write_reg(tg, imm)),
LI64 => handler!(self, |OpsRD(tg, imm)| self.write_reg(tg, imm)), I::LI64 => handler!(self, |OpsRD(tg, imm)| self.write_reg(tg, imm)),
LRA => handler!(self, |OpsRRO(tg, reg, off)| self.write_reg( I::LRA => handler!(self, |OpsRRO(tg, reg, off)| self.write_reg(
tg, tg,
self.pcrel(off).wrapping_add(self.read_reg(reg).cast::<i64>()).get(), self.pcrel(off).wrapping_add(self.read_reg(reg).cast::<i64>()).get(),
)), )),
// Load. If loading more than register size, continue on adjecent registers // Load. If loading more than register size, continue on adjecent registers
LD => handler!(self, |OpsRRAH(dst, base, off, count)| self I::LD => handler!(self, |OpsRRAH(dst, base, off, count)| self
.load(dst, base, off, count)?), .load(dst, base, off, count)?),
// Store. Same rules apply as to LD // Store. Same rules apply as to LD
ST => handler!(self, |OpsRRAH(dst, base, off, count)| self I::ST => handler!(self, |OpsRRAH(dst, base, off, count)| self
.store(dst, base, off, count)?), .store(dst, base, off, count)?),
LDR => handler!(self, |OpsRROH(dst, base, off, count)| self.load( I::LDR => handler!(self, |OpsRROH(dst, base, off, count)| self.load(
dst, dst,
base, base,
self.pcrel(off).get(), self.pcrel(off).get(),
count count
)?), )?),
STR => handler!(self, |OpsRROH(dst, base, off, count)| self.store( I::STR => handler!(self, |OpsRROH(dst, base, off, count)| self.store(
dst, dst,
base, base,
self.pcrel(off).get(), self.pcrel(off).get(),
count count
)?), )?),
BMC => { I::BMC => {
// Block memory copy // Block memory copy
match if let Some(copier) = &mut self.copier { match if let Some(copier) = &mut self.copier {
// There is some copier, poll. // There is some copier, poll.
@ -227,7 +234,7 @@ where
core::task::Poll::Pending => (), core::task::Poll::Pending => (),
} }
} }
BRC => handler!(self, |OpsRRB(src, dst, count)| { I::BRC => handler!(self, |OpsRRB(src, dst, count)| {
// Block register copy // Block register copy
if src.checked_add(count).is_none() || dst.checked_add(count).is_none() { if src.checked_add(count).is_none() || dst.checked_add(count).is_none() {
return Err(VmRunError::RegOutOfBounds); return Err(VmRunError::RegOutOfBounds);
@ -239,11 +246,11 @@ where
usize::from(count), usize::from(count),
); );
}), }),
JMP => { I::JMP => {
let OpsO(off) = self.decode(); let OpsO(off) = self.decode();
self.pc = self.pc.wrapping_add(off); self.pc = self.pc.wrapping_add(off);
} }
JAL => { I::JAL => {
// Jump and link. Save PC after this instruction to // Jump and link. Save PC after this instruction to
// specified register and jump to reg + relative offset. // specified register and jump to reg + relative offset.
let OpsRRO(save, reg, offset) = self.decode(); let OpsRRO(save, reg, offset) = self.decode();
@ -251,7 +258,7 @@ where
self.write_reg(save, self.pc.next::<OpsRRO>()); self.write_reg(save, self.pc.next::<OpsRRO>());
self.pc = self.pcrel(offset).wrapping_add(self.read_reg(reg).cast::<i64>()); self.pc = self.pcrel(offset).wrapping_add(self.read_reg(reg).cast::<i64>());
} }
JALA => { I::JALA => {
// Jump and link. Save PC after this instruction to // Jump and link. Save PC after this instruction to
// specified register and jump to reg // specified register and jump to reg
let OpsRRA(save, reg, offset) = self.decode(); let OpsRRA(save, reg, offset) = self.decode();
@ -261,8 +268,8 @@ where
Address::new(self.read_reg(reg).cast::<u64>().wrapping_add(offset)); Address::new(self.read_reg(reg).cast::<u64>().wrapping_add(offset));
} }
// Conditional jumps, jump only to immediates // Conditional jumps, jump only to immediates
JEQ => self.cond_jmp::<u64>(Ordering::Equal), I::JEQ => self.cond_jmp::<u64>(Ordering::Equal),
JNE => { I::JNE => {
let OpsRRP(a0, a1, ja) = self.decode(); let OpsRRP(a0, a1, ja) = self.decode();
if self.read_reg(a0).cast::<u64>() != self.read_reg(a1).cast::<u64>() { if self.read_reg(a0).cast::<u64>() != self.read_reg(a1).cast::<u64>() {
self.pc = self.pcrel(ja); self.pc = self.pcrel(ja);
@ -270,11 +277,11 @@ where
self.bump_pc::<OpsRRP>(); self.bump_pc::<OpsRRP>();
} }
} }
JLTS => self.cond_jmp::<i64>(Ordering::Less), I::JLTS => self.cond_jmp::<i64>(Ordering::Less),
JGTS => self.cond_jmp::<i64>(Ordering::Greater), I::JGTS => self.cond_jmp::<i64>(Ordering::Greater),
JLTU => self.cond_jmp::<u64>(Ordering::Less), I::JLTU => self.cond_jmp::<u64>(Ordering::Less),
JGTU => self.cond_jmp::<u64>(Ordering::Greater), I::JGTU => self.cond_jmp::<u64>(Ordering::Greater),
ECA => { I::ECA => {
// So we don't get timer interrupt after ECALL // So we don't get timer interrupt after ECALL
if TIMER_QUOTIENT != 0 { if TIMER_QUOTIENT != 0 {
self.timer = self.timer.wrapping_add(1); self.timer = self.timer.wrapping_add(1);
@ -283,33 +290,33 @@ where
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Ok(VmRunOk::Ecall); return Ok(VmRunOk::Ecall);
} }
EBP => { I::EBP => {
self.bump_pc::<OpsN>(); self.bump_pc::<OpsN>();
return Ok(VmRunOk::Breakpoint); return Ok(VmRunOk::Breakpoint);
} }
FADD32 => self.binary_op::<f32>(ops::Add::add), I::FADD32 => self.binary_op::<f32>(ops::Add::add),
FADD64 => self.binary_op::<f64>(ops::Add::add), I::FADD64 => self.binary_op::<f64>(ops::Add::add),
FSUB32 => self.binary_op::<f32>(ops::Sub::sub), I::FSUB32 => self.binary_op::<f32>(ops::Sub::sub),
FSUB64 => self.binary_op::<f64>(ops::Sub::sub), I::FSUB64 => self.binary_op::<f64>(ops::Sub::sub),
FMUL32 => self.binary_op::<f32>(ops::Mul::mul), I::FMUL32 => self.binary_op::<f32>(ops::Mul::mul),
FMUL64 => self.binary_op::<f64>(ops::Mul::mul), I::FMUL64 => self.binary_op::<f64>(ops::Mul::mul),
FDIV32 => self.binary_op::<f32>(ops::Div::div), I::FDIV32 => self.binary_op::<f32>(ops::Div::div),
FDIV64 => self.binary_op::<f64>(ops::Div::div), I::FDIV64 => self.binary_op::<f64>(ops::Div::div),
FMA32 => self.fma::<f32>(), I::FMA32 => self.fma::<f32>(),
FMA64 => self.fma::<f64>(), I::FMA64 => self.fma::<f64>(),
FINV32 => handler!(self, |OpsRR(tg, reg)| self I::FINV32 => handler!(self, |OpsRR(tg, reg)| self
.write_reg(tg, 1. / self.read_reg(reg).cast::<f32>())), .write_reg(tg, 1. / self.read_reg(reg).cast::<f32>())),
FINV64 => handler!(self, |OpsRR(tg, reg)| self I::FINV64 => handler!(self, |OpsRR(tg, reg)| self
.write_reg(tg, 1. / self.read_reg(reg).cast::<f64>())), .write_reg(tg, 1. / self.read_reg(reg).cast::<f64>())),
FCMPLT32 => self.fcmp::<f32>(Ordering::Less), I::FCMPLT32 => self.fcmp::<f32>(Ordering::Less),
FCMPLT64 => self.fcmp::<f64>(Ordering::Less), I::FCMPLT64 => self.fcmp::<f64>(Ordering::Less),
FCMPGT32 => self.fcmp::<f32>(Ordering::Greater), I::FCMPGT32 => self.fcmp::<f32>(Ordering::Greater),
FCMPGT64 => self.fcmp::<f64>(Ordering::Greater), I::FCMPGT64 => self.fcmp::<f64>(Ordering::Greater),
ITF32 => handler!(self, |OpsRR(tg, reg)| self I::ITF32 => handler!(self, |OpsRR(tg, reg)| self
.write_reg(tg, self.read_reg(reg).cast::<i64>() as f32)), .write_reg(tg, self.read_reg(reg).cast::<i64>() as f32)),
ITF64 => handler!(self, |OpsRR(tg, reg)| self I::ITF64 => handler!(self, |OpsRR(tg, reg)| self
.write_reg(tg, self.read_reg(reg).cast::<i64>() as f64)), .write_reg(tg, self.read_reg(reg).cast::<i64>() as f64)),
FTI32 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg( I::FTI32 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg(
tg, tg,
crate::float::f32toint( crate::float::f32toint(
self.read_reg(reg).cast::<f32>(), self.read_reg(reg).cast::<f32>(),
@ -317,7 +324,7 @@ where
.map_err(|()| VmRunError::InvalidOperand)?, .map_err(|()| VmRunError::InvalidOperand)?,
), ),
)), )),
FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg( I::FTI64 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg(
tg, tg,
crate::float::f64toint( crate::float::f64toint(
self.read_reg(reg).cast::<f64>(), self.read_reg(reg).cast::<f64>(),
@ -325,9 +332,9 @@ where
.map_err(|()| VmRunError::InvalidOperand)?, .map_err(|()| VmRunError::InvalidOperand)?,
), ),
)), )),
FC32T64 => handler!(self, |OpsRR(tg, reg)| self I::FC32T64 => handler!(self, |OpsRR(tg, reg)| self
.write_reg(tg, self.read_reg(reg).cast::<f32>() as f64)), .write_reg(tg, self.read_reg(reg).cast::<f32>() as f64)),
FC64T32 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg( I::FC64T32 => handler!(self, |OpsRRB(tg, reg, mode)| self.write_reg(
tg, tg,
crate::float::conv64to32( crate::float::conv64to32(
self.read_reg(reg).cast(), self.read_reg(reg).cast(),
@ -335,27 +342,26 @@ where
.map_err(|()| VmRunError::InvalidOperand)?, .map_err(|()| VmRunError::InvalidOperand)?,
), ),
)), )),
LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| self.write_reg( I::LRA16 => handler!(self, |OpsRRP(tg, reg, imm)| self.write_reg(
tg, tg,
(self.pc + self.read_reg(reg).cast::<u64>() + imm + 3_u16).get(), (self.pc + self.read_reg(reg).cast::<u64>() + imm + 3_u16).get(),
)), )),
LDR16 => handler!(self, |OpsRRPH(dst, base, off, count)| self.load( I::LDR16 => handler!(self, |OpsRRPH(dst, base, off, count)| self.load(
dst, dst,
base, base,
self.pcrel(off).get(), self.pcrel(off).get(),
count count
)?), )?),
STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| self.store( I::STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| self.store(
dst, dst,
base, base,
self.pcrel(off).get(), self.pcrel(off).get(),
count count
)?), )?),
JMP16 => { I::JMP16 => {
let OpsP(off) = self.decode(); let OpsP(off) = self.decode();
self.pc = self.pcrel(off); self.pc = self.pcrel(off);
} }
op => return Err(VmRunError::InvalidOpcode(op)),
} }
} }