Compare commits

...

2 commits

Author SHA1 Message Date
mlokr b8ff503c14
fixing report bug 2024-09-20 11:01:10 +02:00
mlokr 9e69e53e24
merge 2024-09-20 08:20:48 +02:00
19 changed files with 741 additions and 701 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-*

3
Cargo.lock generated
View file

@ -20,6 +20,7 @@ version = "0.1.0"
name = "hblang" name = "hblang"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hbbytecode",
"hbvm", "hbvm",
"regalloc2", "regalloc2",
] ]
@ -57,7 +58,7 @@ dependencies = [
[[package]] [[package]]
name = "regalloc2" name = "regalloc2"
version = "0.10.2" version = "0.10.2"
source = "git+https://github.com/jakubDoka/regalloc2#7e74b2fde4f022816cded93ab5685e46f8e3a159" source = "git+https://github.com/jakubDoka/regalloc2#52b2bbe908e78af1715de88f562f62a83e36ca96"
dependencies = [ dependencies = [
"hashbrown", "hashbrown",
"rustc-hash", "rustc-hash",

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,183 @@ 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
|| prev
.get(global_offset as usize)
.map_or(true, |&b| instr_from_byte(b).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,5 +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" } 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

@ -27,6 +27,7 @@ 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},
@ -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 {
@ -715,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()
@ -744,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 {
@ -857,181 +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;
}
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.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>>,
} }

View file

@ -102,7 +102,7 @@ impl<'a, 'b> Parser<'a, 'b> {
report_to( report_to(
self.lexer.source(), self.lexer.source(),
self.path, self.path,
id.ident, ident::pos(id.ident),
format_args!("undeclared identifier: {}", self.lexer.slice(ident::range(id.ident))), format_args!("undeclared identifier: {}", self.lexer.slice(ident::range(id.ident))),
&mut errors, &mut errors,
); );

View file

@ -1280,6 +1280,9 @@ impl Codegen {
let inps = [VOID, lhs, rhs]; let inps = [VOID, lhs, rhs];
Some(self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, inps)) Some(self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, inps))
} }
Expr::UnOp { pos, op: TokenKind::Band, val } => {
todo!()
}
Expr::UnOp { pos, op, val } => { Expr::UnOp { pos, op, val } => {
let val = self.expr_ctx(val, ctx)?; let val = self.expr_ctx(val, ctx)?;
if !self.tof(val).is_integer() { if !self.tof(val).is_integer() {
@ -1763,20 +1766,23 @@ impl Codegen {
let mut nodes = std::mem::take(&mut self.ci.nodes); let mut nodes = std::mem::take(&mut self.ci.nodes);
let func = Function::new(&mut nodes, &self.tys, sig); let func = Function::new(&mut nodes, &self.tys, sig);
let env = regalloc2::MachineEnv { let mut env = regalloc2::MachineEnv {
preferred_regs_by_class: [ preferred_regs_by_class: [
(1..13).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), (1..12).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(),
vec![], vec![],
vec![], vec![],
], ],
non_preferred_regs_by_class: [ non_preferred_regs_by_class: [
(13..64).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(), (12..64).map(|i| regalloc2::PReg::new(i, regalloc2::RegClass::Int)).collect(),
vec![], vec![],
vec![], vec![],
], ],
scratch_by_class: Default::default(), scratch_by_class: Default::default(),
fixed_stack_slots: Default::default(), fixed_stack_slots: Default::default(),
}; };
if self.ci.call_count != 0 {
std::mem::swap(&mut env.preferred_regs_by_class, &mut env.non_preferred_regs_by_class);
};
let options = regalloc2::RegallocOptions { verbose_log: false, validate_ssa: true }; let options = regalloc2::RegallocOptions { verbose_log: false, validate_ssa: true };
let output = regalloc2::run(&func, &env, &options).unwrap_or_else(|err| panic!("{err}")); let output = regalloc2::run(&func, &env, &options).unwrap_or_else(|err| panic!("{err}"));
@ -2270,9 +2276,18 @@ impl<'a> Function<'a> {
self.emit_node(node.outputs[0], nid); self.emit_node(node.outputs[0], nid);
} }
Kind::CInt { .. } => { Kind::CInt { .. } => {
let unused = node.outputs.into_iter().all(|o| {
let ond = &self.nodes[o];
matches!(ond.kind, Kind::BinOp { op }
if op.imm_binop(ond.ty.is_signed(), 8).is_some()
&& op.cond_op(ond.ty.is_signed()).is_none())
});
if !unused {
let ops = vec![self.drg(nid)]; let ops = vec![self.drg(nid)];
self.add_instr(nid, ops); self.add_instr(nid, ops);
} }
}
Kind::Phi => {} Kind::Phi => {}
Kind::Tuple { index } => { Kind::Tuple { index } => {
let is_start = self.nodes[node.inputs[0]].kind == Kind::Start && index == 0; let is_start = self.nodes[node.inputs[0]].kind == Kind::Start && index == 0;
@ -2747,7 +2762,7 @@ mod tests {
if_statements => README; if_statements => README;
loops => README; loops => README;
fb_driver => README; fb_driver => README;
//pointers => README; pointers => README;
//structs => README; //structs => README;
//different_types => README; //different_types => README;
//struct_operators => README; //struct_operators => README;
@ -2768,8 +2783,15 @@ mod tests {
//inline => README; //inline => README;
//inline_test => README; //inline_test => README;
const_folding_with_arg => README; const_folding_with_arg => README;
// FIXME: contains redundant copies
branch_assignments => README; branch_assignments => README;
exhaustive_loop_testing => README; exhaustive_loop_testing => README;
//idk => README;
//comptime_min_reg_leak => README;
//some_generic_code => README;
//integer_inference_issues => README;
//writing_into_string => README;
//request_page => README;
//tests_ptr_to_ptr_copy => README;
//wide_ret => README;
} }
} }

View file

@ -1,16 +1,16 @@
main: main:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 16h ST r31, r254, 0a, 8h
LI64 r1, 1d LI64 r1, 1d
JNE r2, r1, :0 JNE r2, r1, :0
JMP :1 JMP :1
0: LI64 r32, 0d 0: LI64 r7, 0d
JNE r2, r32, :2 JNE r2, r7, :2
LI64 r1, 2d LI64 r1, 2d
JMP :1 JMP :1
2: LI64 r1, 3d 2: LI64 r1, 3d
1: LD r31, r254, 0a, 16h 1: LD r31, r254, 0a, 8h
ADDI64 r254, r254, 16d ADDI64 r254, r254, 8d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 127 code size: 127
ret: 2 ret: 2

View file

@ -1,18 +1,17 @@
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
JAL r31, r0, :foo
CP r32, r1
LI64 r1, 0d
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
foo: foo:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h ST r31, r254, 0a, 8h
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
code size: 146 main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :foo
LI64 r1, 0d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 143
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,103 +1,106 @@
continue_and_state_change:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
LI64 r32, 3d
LI64 r33, 4d
LI64 r34, 2d
LI64 r35, 10d
6: JLTU r2, r35, :0
CP r1, r2
JMP :1
0: JNE r2, r34, :2
CP r2, r33
JMP :3
2: JNE r2, r32, :4
LI64 r1, 0d
1: JMP :5
4: ADDI64 r2, r2, 1d
3: JMP :6
5: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
main: main:
ADDI64 r254, r254, -88d ADDI64 r254, r254, -72d
ST r31, r254, 0a, 88h ST r31, r254, 0a, 72h
LI64 r32, 0d LI64 r12, 0d
CP r2, r32 CP r2, r12
JAL r31, r0, :multiple_breaks JAL r31, r0, :multiple_breaks
CP r32, r12
CP r33, r1 CP r33, r1
LI64 r34, 3d LI64 r1, 3d
JEQ r33, r34, :0 JEQ r33, r1, :0
LI64 r1, 1d LI64 r1, 1d
JMP :1 JMP :1
0: LI64 r35, 4d 0: CP r34, r1
LI64 r35, 4d
CP r2, r35 CP r2, r35
JAL r31, r0, :multiple_breaks JAL r31, r0, :multiple_breaks
CP r36, r1 CP r36, r35
LI64 r37, 10d LI64 r37, 10d
JEQ r36, r37, :2 JEQ r1, r37, :2
LI64 r1, 2d LI64 r1, 2d
JMP :1 JMP :1
2: CP r2, r32 2: CP r2, r32
JAL r31, r0, :state_change_in_break JAL r31, r0, :state_change_in_break
CP r38, r1 CP r38, r1
JEQ r38, r32, :3 CP r1, r32
JEQ r38, r1, :3
CP r1, r34 CP r1, r34
JMP :1 JMP :1
3: CP r2, r35 3: CP r32, r1
CP r2, r36
JAL r31, r0, :state_change_in_break JAL r31, r0, :state_change_in_break
CP r39, r1 JEQ r1, r37, :4
JEQ r39, r37, :4 CP r1, r36
CP r1, r35
JMP :1 JMP :1
4: CP r2, r37 4: CP r2, r37
JAL r31, r0, :continue_and_state_change JAL r31, r0, :continue_and_state_change
CP r40, r1 JEQ r1, r37, :5
JEQ r40, r37, :5
LI64 r1, 5d LI64 r1, 5d
JMP :1 JMP :1
5: CP r2, r34 5: CP r2, r34
JAL r31, r0, :continue_and_state_change JAL r31, r0, :continue_and_state_change
CP r41, r1 CP r39, r1
JEQ r41, r32, :6 CP r1, r32
JEQ r39, r1, :6
LI64 r1, 6d LI64 r1, 6d
JMP :1 JMP :1
6: CP r1, r32 6: CP r1, r32
1: LD r31, r254, 0a, 88h 1: LD r31, r254, 0a, 72h
ADDI64 r254, r254, 88d ADDI64 r254, r254, 72d
JALA r0, r31, 0a
continue_and_state_change:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
CP r1, r2
LI64 r32, 3d
LI64 r33, 4d
LI64 r34, 2d
LI64 r35, 10d
6: JLTU r1, r35, :0
JMP :1
0: JNE r1, r34, :2
CP r1, r33
JMP :3
2: JNE r1, r32, :4
LI64 r1, 0d
1: JMP :5
4: ADDI64 r33, r1, 1d
CP r1, r33
3: JMP :6
5: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
state_change_in_break:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
CP r1, r2
LI64 r32, 3d
LI64 r33, 10d
4: JLTU r1, r33, :0
JMP :1
0: JNE r1, r32, :2
LI64 r1, 0d
1: JMP :3
2: ADDI64 r1, r1, 1d
JMP :4
3: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a JALA r0, r31, 0a
multiple_breaks: multiple_breaks:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
CP r1, r2
LI64 r32, 3d LI64 r32, 3d
LI64 r33, 10d LI64 r33, 10d
4: JLTU r1, r33, :0 4: JLTU r2, r33, :0
CP r1, r2
JMP :1 JMP :1
0: ADDI64 r1, r1, 1d 0: ADDI64 r1, r2, 1d
JNE r1, r32, :2 JNE r1, r32, :2
1: JMP :3 1: JMP :3
2: JMP :4 2: CP r2, r1
JMP :4
3: LD r31, r254, 0a, 24h 3: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d ADDI64 r254, r254, 24d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 704 state_change_in_break:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
LI64 r32, 3d
LI64 r33, 10d
4: JLTU r2, r33, :0
CP r1, r2
JMP :1
0: JNE r2, r32, :2
LI64 r1, 0d
1: JMP :3
2: ADDI64 r2, r2, 1d
JMP :4
3: LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
code size: 713
ret: 0 ret: 0
status: Ok(()) status: Ok(())

View file

@ -1,37 +1,45 @@
check_platform:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :x86_fb_ptr
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
main: main:
ADDI64 r254, r254, -88d ADDI64 r254, r254, -64d
ST r31, r254, 0a, 88h ST r31, r254, 0a, 64h
JAL r31, r0, :check_platform JAL r31, r0, :check_platform
CP r32, r1 LI64 r32, 0d
LI64 r33, 0d LI64 r33, 30d
LI64 r34, 30d LI64 r34, 100d
LI64 r35, 100d CP r35, r32
CP r36, r33 CP r36, r32
CP r37, r33 CP r37, r32
CP r38, r33 5: JLTU r35, r33, :0
5: JLTU r37, r34, :0
ADDI64 r36, r36, 1d ADDI64 r36, r36, 1d
CP r2, r33 CP r2, r32
CP r3, r36 CP r3, r36
CP r4, r34 CP r4, r33
JAL r31, r0, :set_pixel JAL r31, r0, :set_pixel
CP r39, r1 JEQ r1, r37, :1
JEQ r39, r38, :1 CP r1, r32
CP r1, r33
JMP :2 JMP :2
1: JNE r36, r35, :3 1: CP r38, r32
CP r1, r38 JNE r36, r34, :3
CP r1, r37
JMP :2 JMP :2
3: CP r37, r33 3: CP r1, r37
CP r40, r38 CP r35, r38
JMP :4 JMP :4
0: ADDI64 r40, r38, 1d 0: CP r1, r37
ADDI64 r41, r37, 1d CP r38, r32
CP r37, r41 ADDI64 r1, r1, 1d
4: CP r38, r40 ADDI64 r35, r35, 1d
4: CP r32, r38
CP r37, r1
JMP :5 JMP :5
2: LD r31, r254, 0a, 88h 2: LD r31, r254, 0a, 64h
ADDI64 r254, r254, 88d ADDI64 r254, r254, 64d
JALA r0, r31, 0a JALA r0, r31, 0a
set_pixel: set_pixel:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -16d
@ -41,13 +49,6 @@ set_pixel:
LD r31, r254, 0a, 16h LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d ADDI64 r254, r254, 16d
JALA r0, r31, 0a JALA r0, r31, 0a
check_platform:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
JAL r31, r0, :x86_fb_ptr
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
x86_fb_ptr: x86_fb_ptr:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h ST r31, r254, 0a, 8h
@ -55,6 +56,6 @@ x86_fb_ptr:
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
code size: 423 code size: 426
ret: 3000 ret: 3000
status: Ok(()) status: Ok(())

View file

@ -1,15 +1,9 @@
main: add_one:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -8d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 8h
LI64 r2, 10d ADDI64 r1, r2, 1d
JAL r31, r0, :add_one LD r31, r254, 0a, 8h
CP r32, r1 ADDI64 r254, r254, 8d
LI64 r2, 20d
JAL r31, r0, :add_two
CP r33, r1
ADD64 r1, r33, r32
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a JALA r0, r31, 0a
add_two: add_two:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -8d
@ -18,13 +12,18 @@ add_two:
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
add_one: main:
ADDI64 r254, r254, -8d ADDI64 r254, r254, -16d
ST r31, r254, 0a, 8h ST r31, r254, 0a, 16h
ADDI64 r1, r2, 1d LI64 r2, 10d
LD r31, r254, 0a, 8h JAL r31, r0, :add_one
ADDI64 r254, r254, 8d CP r32, r1
LI64 r2, 20d
JAL r31, r0, :add_two
ADD64 r1, r1, r32
LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 251 code size: 248
ret: 33 ret: 33
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,22 @@
fib:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
LI64 r1, 1d
LI64 r32, 2d
JGTU r2, r32, :0
JMP :1
0: CP r33, r2
ADDI64 r2, r33, -1d
CP r34, r33
JAL r31, r0, :fib
CP r2, r34
CP r35, r1
ADDI64 r2, r2, -2d
JAL r31, r0, :fib
ADD64 r1, r1, r35
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
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,24 +25,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
fib: code size: 225
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
CP r32, r2
LI64 r1, 1d
LI64 r33, 2d
JGTU r32, r33, :0
JMP :1
0: SUB64 r2, r32, r1
JAL r31, r0, :fib
CP r34, r1
SUB64 r2, r32, r33
JAL r31, r0, :fib
CP r35, r1
ADD64 r1, r35, r34
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 208
ret: 55 ret: 55
status: Ok(()) status: Ok(())

View file

@ -1,3 +1,19 @@
fib:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
LI64 r32, 1d
LI64 r33, 0d
CP r1, r33
2: JNE r2, r33, :0
JMP :1
0: ADD64 r34, r32, r1
ADDI64 r2, r2, -1d
CP r1, r32
CP r32, r34
JMP :2
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
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,23 +22,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
fib: code size: 202
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
LI64 r32, 1d
LI64 r33, 0d
CP r1, r33
CP r34, r32
2: JNE r2, r33, :0
JMP :1
0: ADD64 r35, r34, r1
SUB64 r2, r2, r32
CP r1, r34
CP r34, r35
JMP :2
1: LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
code size: 198
ret: 55 ret: 55
status: Ok(()) status: Ok(())

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