1
0
Fork 0
forked from koniifer/ableos

Compare commits

...

12 commits

Author SHA1 Message Date
mlokr 936359b896 removing hbcb 2024-09-20 07:59:40 +02:00
mlokr e9b5baa900 brah 2024-09-19 19:18:22 +02:00
mlokr 4303cd1aed moving codegen to instrs alongside disasm 2024-09-19 18:27:25 +02:00
mlokr 6b454b2862 merge 2024-09-19 17:15:03 +02:00
mlokr 1a5d77b8f4 reducing dependencies 2024-09-19 17:13:51 +02:00
mlokr 2999a011f5 implementing ableos executable format 2024-09-19 13:40:03 +02:00
mlokr 65c99ff74c removing git support and relative path prefix which did nothing anyway 2024-09-18 10:34:07 +02:00
mlokr 90f623976e improving one particular error message 2024-09-18 10:14:17 +02:00
mlokr b42824b460 triing to turn absolute to relative paths in error messages 2024-09-18 10:07:40 +02:00
mlokr 239852db86 improving parser error messages 2024-09-18 09:47:52 +02:00
mlokr bb32558b74 blah 2024-09-17 15:14:24 +02:00
mlokr e862c3e4f7 initiating cranelift backend 2024-09-17 10:39:56 +02:00
28 changed files with 1497 additions and 4100 deletions

7
.gitignore vendored
View file

@ -1,7 +1,4 @@
/target /target
/hbbytecode/src/opcode.rs /hbbytecode/src/instrs.rs
/hbbytecode/src/ops.rs
/hblang/src/instrs.rs
/hblang/src/disasm.rs
/.rgignore /.rgignore
rust-ice-* rustc-ice-*

30
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]] [[package]]
name = "hbbytecode" name = "hbbytecode"
version = "0.1.0" version = "0.1.0"
@ -14,7 +20,9 @@ version = "0.1.0"
name = "hblang" name = "hblang"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hbbytecode",
"hbvm", "hbvm",
"regalloc2",
] ]
[[package]] [[package]]
@ -47,6 +55,28 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "regalloc2"
version = "0.10.2"
source = "git+https://github.com/jakubDoka/regalloc2#7e74b2fde4f022816cded93ab5685e46f8e3a159"
dependencies = [
"hashbrown",
"rustc-hash",
"smallvec",
]
[[package]]
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "xtask" name = "xtask"
version = "0.1.0" version = "0.1.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')
}

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

@ -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

@ -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

@ -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,18 +1237,33 @@ 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)]
pub struct Ast(NonNull<AstInner<[Symbol]>>); pub struct Ast(NonNull<AstInner<[Symbol]>>);

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,19 +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:
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
code size: 341 code size: 341
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

@ -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)),
} }
} }

2411
ited:

File diff suppressed because it is too large Load diff