2024-02-22 16:57:29 -06:00
|
|
|
|
//! Functions for inserting instructions
|
|
|
|
|
//!
|
|
|
|
|
//! Most of the code you see is just metaprogramming stuff.
|
|
|
|
|
//! This ensures that adding new instructions won't need any
|
|
|
|
|
//! specific changes and consistent behaviour.
|
|
|
|
|
//!
|
|
|
|
|
//! > I tried to comment stuff here, but I meanwhile forgor how it works.
|
|
|
|
|
//!
|
|
|
|
|
//! — Erin
|
|
|
|
|
|
2023-10-22 08:08:45 -05:00
|
|
|
|
use {
|
2023-10-27 20:29:02 -05:00
|
|
|
|
crate::object::Object,
|
2024-02-14 04:45:58 -06:00
|
|
|
|
rhai::{FuncRegistration, Module},
|
2023-10-27 20:29:02 -05:00
|
|
|
|
std::{cell::RefCell, rc::Rc},
|
2023-10-22 08:08:45 -05:00
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Operand types and their insertions
|
2024-01-31 10:54:38 -06:00
|
|
|
|
pub mod optypes {
|
2023-10-27 20:29:02 -05:00
|
|
|
|
use {
|
|
|
|
|
crate::{
|
|
|
|
|
label::UnboundLabel,
|
|
|
|
|
object::{Object, RelocKey, RelocType, SymbolRef},
|
|
|
|
|
},
|
|
|
|
|
rhai::{Dynamic, EvalAltResult, ImmutableString, Position},
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// These types represent operand types to be inserted
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub type R = u8;
|
2023-10-22 08:08:45 -05:00
|
|
|
|
pub type B = i8;
|
|
|
|
|
pub type H = i16;
|
|
|
|
|
pub type W = i32;
|
|
|
|
|
pub type D = i64;
|
|
|
|
|
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub type A = Dynamic;
|
|
|
|
|
pub type O = Dynamic;
|
|
|
|
|
pub type P = Dynamic;
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Insert relocation into code
|
|
|
|
|
///
|
|
|
|
|
/// - If integer, just write it to the code
|
|
|
|
|
/// - Otherwise insert entry into relocation table
|
|
|
|
|
/// and fill zeroes
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub fn insert_reloc(
|
|
|
|
|
obj: &mut Object,
|
|
|
|
|
ty: RelocType,
|
|
|
|
|
val: &Dynamic,
|
|
|
|
|
) -> Result<(), EvalAltResult> {
|
|
|
|
|
match () {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Direct references – insert directly to table
|
2023-10-27 20:29:02 -05:00
|
|
|
|
_ if val.is::<SymbolRef>() => {
|
|
|
|
|
obj.relocation(RelocKey::Symbol(val.clone_cast::<SymbolRef>().0), ty)
|
|
|
|
|
}
|
|
|
|
|
_ if val.is::<UnboundLabel>() => {
|
|
|
|
|
obj.relocation(RelocKey::Symbol(val.clone_cast::<UnboundLabel>().0), ty)
|
|
|
|
|
}
|
|
|
|
|
_ if val.is::<DataRef>() => {
|
|
|
|
|
obj.relocation(RelocKey::Symbol(val.clone_cast::<DataRef>().symbol.0), ty)
|
|
|
|
|
}
|
2024-02-22 16:57:29 -06:00
|
|
|
|
|
|
|
|
|
// String (indirect) reference
|
2023-10-27 20:29:02 -05:00
|
|
|
|
_ if val.is_string() => {
|
|
|
|
|
obj.relocation(RelocKey::Label(val.clone_cast::<ImmutableString>()), ty)
|
|
|
|
|
}
|
2024-02-22 16:57:29 -06:00
|
|
|
|
|
|
|
|
|
// Manual offset
|
2023-10-27 20:29:02 -05:00
|
|
|
|
_ if val.is_int() => {
|
|
|
|
|
let int = val.clone_cast::<i64>();
|
|
|
|
|
match ty {
|
|
|
|
|
RelocType::Rel32 => obj.sections.text.extend((int as i32).to_le_bytes()),
|
|
|
|
|
RelocType::Rel16 => obj.sections.text.extend((int as i16).to_le_bytes()),
|
|
|
|
|
RelocType::Abs64 => obj.sections.text.extend(int.to_le_bytes()),
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-22 16:57:29 -06:00
|
|
|
|
|
2023-10-27 20:29:02 -05:00
|
|
|
|
_ => {
|
|
|
|
|
return Err(EvalAltResult::ErrorMismatchDataType(
|
2024-02-22 16:57:29 -06:00
|
|
|
|
"SymbolRef, UnboundLabel, String or Int".to_owned(),
|
2023-10-27 20:29:02 -05:00
|
|
|
|
val.type_name().to_owned(),
|
|
|
|
|
Position::NONE,
|
|
|
|
|
))
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 20:29:02 -05:00
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Generate macro for inserting item into the output object
|
|
|
|
|
///
|
|
|
|
|
/// Pre-defines inserts for absolute address and relative offsets.
|
|
|
|
|
/// These are inserted with function [`insert_reloc`]
|
|
|
|
|
/// # le_bytes
|
|
|
|
|
/// `gen_insert!(le_bytes: [B, …]);`
|
|
|
|
|
///
|
|
|
|
|
/// Takes sequence of operand types which should be inserted
|
|
|
|
|
/// by invoking `to_le_bytes` method on it.
|
2023-10-27 20:29:02 -05:00
|
|
|
|
macro_rules! gen_insert {
|
|
|
|
|
(le_bytes: [$($lety:ident),* $(,)?]) => {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// `insert!($thing, $obj, $type)` where
|
|
|
|
|
/// - `$thing`: Value you want to insert
|
|
|
|
|
/// - `$obj`: Code object
|
|
|
|
|
/// - `$type`: Type of inserted value
|
|
|
|
|
///
|
|
|
|
|
/// Eg. `insert!(69_u8, obj, B);`
|
2023-10-27 20:29:02 -05:00
|
|
|
|
macro_rules! insert {
|
|
|
|
|
$(($thing:expr, $obj: expr, $lety) => {
|
|
|
|
|
$obj.sections.text.extend($thing.to_le_bytes());
|
|
|
|
|
};)*
|
|
|
|
|
|
|
|
|
|
($thing:expr, $obj:expr, A) => {
|
|
|
|
|
$crate::ins::optypes::insert_reloc(
|
|
|
|
|
$obj,
|
|
|
|
|
$crate::object::RelocType::Abs64,
|
|
|
|
|
$thing
|
|
|
|
|
)?
|
|
|
|
|
};
|
|
|
|
|
($thing:expr, $obj:expr, O) => {
|
|
|
|
|
$crate::ins::optypes::insert_reloc(
|
|
|
|
|
$obj,
|
|
|
|
|
$crate::object::RelocType::Rel32,
|
|
|
|
|
$thing
|
|
|
|
|
)?
|
|
|
|
|
};
|
|
|
|
|
($thing:expr, $obj:expr, P) => {
|
|
|
|
|
$crate::ins::optypes::insert_reloc(
|
|
|
|
|
$obj,
|
|
|
|
|
$crate::object::RelocType::Rel16,
|
|
|
|
|
$thing
|
|
|
|
|
)?
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gen_insert!(le_bytes: [R, B, H, W, D]);
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::single_component_path_imports)]
|
|
|
|
|
pub(super) use insert;
|
|
|
|
|
|
|
|
|
|
use crate::data::DataRef;
|
2023-10-22 08:08:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Rhai Types (types for function parameters as Rhai uses only 64bit signed integers)
|
2024-01-31 10:54:38 -06:00
|
|
|
|
pub mod rity {
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub use super::optypes::{A, O, P, R};
|
|
|
|
|
pub type B = i64;
|
|
|
|
|
pub type H = i64;
|
|
|
|
|
pub type W = i64;
|
|
|
|
|
pub type D = i64;
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Generic instruction (instruction of certain operands type) inserts
|
2024-01-31 10:54:38 -06:00
|
|
|
|
pub mod generic {
|
2023-10-27 20:29:02 -05:00
|
|
|
|
use {crate::object::Object, rhai::EvalAltResult};
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub(super) fn convert_op<A, B>(from: A) -> Result<B, EvalAltResult>
|
|
|
|
|
where
|
|
|
|
|
B: TryFrom<A>,
|
|
|
|
|
<B as TryFrom<A>>::Error: std::error::Error + Sync + Send + 'static,
|
|
|
|
|
{
|
|
|
|
|
B::try_from(from).map_err(|e| {
|
|
|
|
|
EvalAltResult::ErrorSystem("Data conversion error".to_owned(), Box::new(e))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Generate opcode-generic instruction insert macro
|
2023-10-27 20:29:02 -05:00
|
|
|
|
macro_rules! gen_ins {
|
|
|
|
|
($($($name:ident : $ty:ty),*;)*) => {
|
2023-10-22 08:08:45 -05:00
|
|
|
|
paste::paste! {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
$(
|
|
|
|
|
/// Instruction-generic opcode insertion function
|
|
|
|
|
/// - `obj`: Code object
|
|
|
|
|
/// - `opcode`: opcode, not checked if valid for instruction type
|
|
|
|
|
/// - … for operands
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn [<$($ty:lower)*>](
|
|
|
|
|
obj: &mut Object,
|
|
|
|
|
opcode: u8,
|
|
|
|
|
$($name: $crate::ins::optypes::$ty),*,
|
|
|
|
|
) -> Result<(), EvalAltResult> {
|
|
|
|
|
// Push opcode
|
|
|
|
|
obj.sections.text.push(opcode);
|
|
|
|
|
|
|
|
|
|
// Insert based on type
|
|
|
|
|
$($crate::ins::optypes::insert!(&$name, obj, $ty);)*
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
)*
|
2023-10-27 20:29:02 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Generate Rhai opcode-specific instruction insertion functions
|
|
|
|
|
///
|
|
|
|
|
/// `gen_ins_fn!($obj, $opcode, $optype);` where:
|
|
|
|
|
/// - `$obj`: Code object
|
|
|
|
|
/// - `$opcode`: Opcode value
|
2023-10-27 20:29:02 -05:00
|
|
|
|
macro_rules! gen_ins_fn {
|
2024-02-14 04:45:58 -06:00
|
|
|
|
$(
|
|
|
|
|
($obj:expr, $opcode:expr, [<$($ty)*>]) => {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Opcode-specific insertion function
|
|
|
|
|
// - Parameters = operands
|
2024-02-14 04:45:58 -06:00
|
|
|
|
move |$($name: $crate::ins::rity::$ty),*| {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Invoke generic function
|
2024-02-14 04:45:58 -06:00
|
|
|
|
$crate::ins::generic::[<$($ty:lower)*>](
|
|
|
|
|
&mut *$obj.borrow_mut(),
|
|
|
|
|
$opcode,
|
|
|
|
|
$(
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Convert to desired type (from Rhai-provided values)
|
|
|
|
|
$crate::ins::generic::convert_op::<
|
2024-02-14 04:45:58 -06:00
|
|
|
|
_,
|
|
|
|
|
$crate::ins::optypes::$ty
|
|
|
|
|
>($name)?
|
|
|
|
|
),*
|
|
|
|
|
)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Internal-use: count args
|
2024-02-14 04:45:58 -06:00
|
|
|
|
(@arg_count [<$($ty)*>]) => {
|
|
|
|
|
{ ["", $(stringify!($ty)),*].len() - 1 }
|
|
|
|
|
};
|
|
|
|
|
)*
|
2023-10-27 20:29:02 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Specialisation for no-operand instructions
|
2023-10-27 20:29:02 -05:00
|
|
|
|
($obj:expr, $opcode:expr, N) => {
|
|
|
|
|
move || {
|
|
|
|
|
$crate::ins::generic::n(&mut *$obj.borrow_mut(), $opcode);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-02-14 04:45:58 -06:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Internal-use specialisation: no-operand instructions
|
2024-02-14 04:45:58 -06:00
|
|
|
|
(@arg_count N) => {
|
|
|
|
|
{ 0 }
|
|
|
|
|
};
|
2023-10-27 20:29:02 -05:00
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
}
|
2023-10-27 20:29:02 -05:00
|
|
|
|
};
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Specialisation for no-operand instructions – simply just push opcode
|
2023-10-27 20:29:02 -05:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn n(obj: &mut Object, opcode: u8) {
|
|
|
|
|
obj.sections.text.push(opcode);
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Generate opcode-generic instruction inserters
|
|
|
|
|
// (operand identifiers are arbitrary)
|
|
|
|
|
//
|
|
|
|
|
// New instruction types have to be added manually here
|
2023-10-27 20:29:02 -05:00
|
|
|
|
gen_ins! {
|
|
|
|
|
o0: R, o1: R;
|
|
|
|
|
o0: R, o1: R, o2: R;
|
|
|
|
|
o0: R, o1: R, o2: R, o3: R;
|
|
|
|
|
o0: R, o1: R, o2: B;
|
|
|
|
|
o0: R, o1: R, o2: H;
|
|
|
|
|
o0: R, o1: R, o2: W;
|
|
|
|
|
o0: R, o1: R, o2: D;
|
|
|
|
|
o0: R, o1: B;
|
|
|
|
|
o0: R, o1: H;
|
|
|
|
|
o0: R, o1: W;
|
|
|
|
|
o0: R, o1: D;
|
|
|
|
|
o0: R, o1: R, o2: A;
|
|
|
|
|
o0: R, o1: R, o2: A, o3: H;
|
|
|
|
|
o0: R, o1: R, o2: O, o3: H;
|
|
|
|
|
o0: R, o1: R, o2: P, o3: H;
|
|
|
|
|
o0: R, o1: R, o2: O;
|
|
|
|
|
o0: R, o1: R, o2: P;
|
|
|
|
|
o0: O;
|
|
|
|
|
o0: P;
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2023-10-27 20:29:02 -05:00
|
|
|
|
#[allow(clippy::single_component_path_imports)]
|
|
|
|
|
pub(super) use gen_ins_fn;
|
2023-10-22 08:08:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Generate instructions from instruction table
|
|
|
|
|
///
|
|
|
|
|
/// ```ignore
|
|
|
|
|
/// instructions!(($module, $obj) {
|
|
|
|
|
/// // Data from instruction table
|
|
|
|
|
/// $opcode, $mnemonic, $opty, $doc;
|
|
|
|
|
/// …
|
|
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
/// - `$module`: Rhai module
|
|
|
|
|
/// - `$obj`: Code object
|
2023-10-27 20:29:02 -05:00
|
|
|
|
macro_rules! instructions {
|
|
|
|
|
(
|
|
|
|
|
($module:expr, $obj:expr $(,)?)
|
2024-02-14 04:45:58 -06:00
|
|
|
|
{ $($opcode:expr, $mnemonic:ident, $ops:tt, $doc:literal;)* }
|
2023-10-27 20:29:02 -05:00
|
|
|
|
) => {{
|
|
|
|
|
let (module, obj) = ($module, $obj);
|
|
|
|
|
$({
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Object is shared across all functions
|
2023-10-27 20:29:02 -05:00
|
|
|
|
let obj = Rc::clone(&obj);
|
2024-02-22 16:57:29 -06:00
|
|
|
|
|
|
|
|
|
// Register newly generated function for each instruction
|
2024-02-14 04:45:58 -06:00
|
|
|
|
FuncRegistration::new(stringify!([<$mnemonic:lower>]))
|
|
|
|
|
.with_namespace(rhai::FnNamespace::Global)
|
|
|
|
|
.set_into_module::<_, { generic::gen_ins_fn!(@arg_count $ops) }, false, _, true, _>(
|
|
|
|
|
module,
|
|
|
|
|
generic::gen_ins_fn!(
|
|
|
|
|
obj,
|
|
|
|
|
$opcode,
|
|
|
|
|
$ops
|
|
|
|
|
)
|
|
|
|
|
);
|
2023-10-27 20:29:02 -05:00
|
|
|
|
})*
|
|
|
|
|
}};
|
|
|
|
|
}
|
2023-10-22 08:08:45 -05:00
|
|
|
|
|
2024-02-22 16:57:29 -06:00
|
|
|
|
/// Setup instruction insertors
|
2023-10-27 20:29:02 -05:00
|
|
|
|
pub fn setup(module: &mut Module, obj: Rc<RefCell<Object>>) {
|
2024-02-22 16:57:29 -06:00
|
|
|
|
// Import instructions table and use it for generation
|
2023-10-22 08:08:45 -05:00
|
|
|
|
with_builtin_macros::with_builtin! {
|
|
|
|
|
let $spec = include_from_root!("../hbbytecode/instructions.in") in {
|
2023-10-27 20:29:02 -05:00
|
|
|
|
instructions!((module, obj) { $spec });
|
2023-10-22 08:08:45 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|