use crate::compiler::Executable;
use crate::CompilerError;
use arrayvec::ArrayVec;
use dynasmrt::mmap::MutableBuffer;
use dynasmrt::{
components::PatchLoc, relocations::Relocation, AssemblyOffset, DynamicLabel, DynasmApi,
DynasmLabelApi,
};
use std::marker::PhantomData;
pub(crate) use dynasmrt::mmap::ExecutableBuffer;
#[derive(Debug, Clone)]
pub(crate) struct Assembler<R: Relocation, const S: usize> {
buffer: ArrayVec<u8, S>,
target: Option<AssemblyOffset>,
phantom: PhantomData<R>,
}
impl<R: Relocation, const S: usize> Assembler<R, S> {
#[inline(always)]
pub(crate) fn entry() -> AssemblyOffset {
AssemblyOffset(0)
}
#[inline(always)]
pub(crate) fn len(&self) -> usize {
self.buffer.len()
}
#[inline(always)]
pub(crate) fn new() -> Self {
Self {
buffer: ArrayVec::new(),
target: None,
phantom: PhantomData,
}
}
#[inline(always)]
pub(crate) fn finalize(&self) -> Result<Executable, CompilerError> {
let mut mut_buf = MutableBuffer::new(self.buffer.len())?;
mut_buf.set_len(self.buffer.len());
mut_buf[..].copy_from_slice(&self.buffer);
Ok(Executable {
buffer: mut_buf.make_exec()?,
})
}
}
impl std::fmt::Debug for Executable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Executable[{}; {} ]",
std::env::consts::ARCH,
hex::encode(&self.buffer[..])
)
}
}
impl<R: Relocation, const S: usize> DynasmLabelApi for Assembler<R, S> {
type Relocation = R;
#[inline(always)]
fn local_label(&mut self, name: &'static str) {
debug_assert_eq!(name, "target");
self.target = Some(self.offset());
}
#[inline(always)]
fn backward_relocation(
&mut self,
name: &'static str,
target_offset: isize,
field_offset: u8,
ref_offset: u8,
kind: R,
) {
debug_assert_eq!(name, "target");
let target = self
.target
.expect("generated programs always have a target before branch");
let loc = PatchLoc::new(self.offset(), target_offset, field_offset, ref_offset, kind);
let buf = &mut self.buffer[loc.range(0)];
loc.patch(buf, 0, target.0)
.expect("program relocations are always in range");
}
fn global_label(&mut self, _name: &'static str) {
unreachable!();
}
fn dynamic_label(&mut self, _id: DynamicLabel) {
unreachable!();
}
fn bare_relocation(&mut self, _target: usize, _field_offset: u8, _ref_offset: u8, _kind: R) {
unreachable!();
}
fn global_relocation(
&mut self,
_name: &'static str,
_target_offset: isize,
_field_offset: u8,
_ref_offset: u8,
_kind: R,
) {
unreachable!();
}
fn dynamic_relocation(
&mut self,
_id: DynamicLabel,
_target_offset: isize,
_field_offset: u8,
_ref_offset: u8,
_kind: R,
) {
unreachable!();
}
fn forward_relocation(
&mut self,
_name: &'static str,
_target_offset: isize,
_field_offset: u8,
_ref_offset: u8,
_kind: R,
) {
unreachable!();
}
}
impl<R: Relocation, const S: usize> Extend<u8> for Assembler<R, S> {
#[inline(always)]
fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
self.buffer.extend(iter);
}
}
impl<'a, R: Relocation, const S: usize> Extend<&'a u8> for Assembler<R, S> {
#[inline(always)]
fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
for byte in iter {
self.buffer.push(*byte);
}
}
}
impl<R: Relocation, const S: usize> DynasmApi for Assembler<R, S> {
#[inline(always)]
fn offset(&self) -> AssemblyOffset {
AssemblyOffset(self.buffer.len())
}
#[inline(always)]
fn push(&mut self, byte: u8) {
self.buffer.push(byte);
}
fn align(&mut self, _alignment: usize, _with: u8) {
unreachable!();
}
}