#[cfg(feature = "jit")]
pub use copypatch::{patch_branch26, patch_x86_rel32};
#[cfg(not(feature = "jit"))]
pub fn patch_branch26(_: &mut [u8], _: usize, _: usize) {
unreachable!("native copy-and-patch is unavailable without Weavy's jit feature")
}
#[cfg(not(feature = "jit"))]
pub fn patch_x86_rel32(_: &mut [u8], _: usize, _: usize) {
unreachable!("native copy-and-patch is unavailable without Weavy's jit feature")
}
#[cfg(weavy_jit_active)]
use copypatch::ExecBuf;
pub mod stencils {
include!(concat!(env!("OUT_DIR"), "/weavy_stencils.rs"));
}
pub mod async_stencils {
include!(concat!(env!("OUT_DIR"), "/weavy_async_stencils.rs"));
}
pub mod task_stencils {
include!(concat!(env!("OUT_DIR"), "/weavy_task_stencils.rs"));
}
pub mod debug;
pub mod dwarf;
pub mod task_lane;
pub const NATIVE_COPY_PATCH_AVAILABLE: bool = cfg!(weavy_jit_active);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NativeUnavailable;
impl core::fmt::Display for NativeUnavailable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("native copy-and-patch execution is unavailable in this build")
}
}
impl std::error::Error for NativeUnavailable {}
#[repr(C)]
pub struct HostCallInfo {
pub info: *const (),
pub call: unsafe extern "C" fn(cx: *mut (), info: *const ()) -> bool,
}
#[repr(C)]
pub struct HostCallCtx<C> {
pub prog: *const u64,
pub inner: *mut C,
}
impl<C> HostCallCtx<C> {
#[must_use]
#[inline]
pub fn new(prog: *const u64, inner: &mut C) -> Self {
Self { prog, inner }
}
}
pub trait HostCall<C> {
fn call(&self, cx: &mut C) -> bool;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct HostCallChainLayout {
pub hostcall_sites: usize,
pub copied_stencils: usize,
}
impl HostCallChainLayout {
#[must_use]
pub const fn for_hostcall_sites(hostcall_sites: usize) -> Self {
Self {
hostcall_sites,
copied_stencils: hostcall_sites + 1,
}
}
}
pub struct HostCallChain<I> {
infos: Vec<I>,
#[cfg(weavy_jit_active)]
call_slots: Vec<ProgSlot>,
#[cfg(weavy_jit_active)]
calls: Vec<HostCallInfo>,
#[cfg(weavy_jit_active)]
native: NativeProgram,
}
unsafe impl<I: Send> Send for HostCallChain<I> {}
impl<I> HostCallChain<I> {
#[must_use]
pub fn new(infos: Vec<I>) -> Self {
#[cfg(not(weavy_jit_active))]
{
Self { infos }
}
#[cfg(weavy_jit_active)]
{
let mut layout = StencilLayout::new();
let root = layout.start_chain();
let mut previous = None;
let mut call_slots = Vec::with_capacity(infos.len());
for _ in &infos {
let slot = layout.reserve_prog_slot(root.prog_index);
call_slots.push(slot);
let current = layout.emit_stencil(stencils::HOSTCALL);
if let Some(previous) = previous {
layout.patch_hostcall_continuation(previous, current);
}
previous = Some(current);
}
let done = layout.emit_done();
if let Some(previous) = previous {
layout.patch_hostcall_continuation(previous, done);
}
Self {
infos,
call_slots,
calls: Vec::new(),
native: NativeProgram::new(layout, root),
}
}
}
pub fn run<C>(&mut self, cx: &mut C) -> Result<(), NativeUnavailable>
where
I: HostCall<C>,
{
#[cfg(not(weavy_jit_active))]
{
let _ = cx;
Err(NativeUnavailable)
}
#[cfg(weavy_jit_active)]
{
let call: unsafe extern "C" fn(*mut (), *const ()) -> bool = typed_hostcall::<C, I>;
let calls_bound = self.calls.len() == self.infos.len()
&& self
.calls
.iter()
.all(|record| core::ptr::fn_addr_eq(record.call, call));
if !calls_bound {
self.calls.clear();
self.calls
.extend(self.infos.iter().map(|info| HostCallInfo {
info: core::ptr::from_ref(info).cast(),
call,
}));
for (slot, call) in self.call_slots.iter().copied().zip(&self.calls) {
self.native
.fill_prog_slot(slot, core::ptr::from_ref(call) as u64);
}
}
let mut host_ctx = HostCallCtx::new(self.native.entry_prog(), cx);
let entry = unsafe { self.native.entry_fn::<HostCallCtx<C>>() };
unsafe {
entry(&mut host_ctx);
}
Ok(())
}
}
#[must_use]
pub fn infos(&self) -> &[I] {
&self.infos
}
#[must_use]
pub fn hostcall_site_count(&self) -> usize {
HostCallChainLayout::for_hostcall_sites(self.infos.len()).hostcall_sites
}
#[must_use]
pub fn hostcall_count(&self) -> usize {
#[cfg(not(weavy_jit_active))]
{
0
}
#[cfg(weavy_jit_active)]
{
self.calls.len()
}
}
#[must_use]
pub fn stencil_count(&self) -> usize {
#[cfg(not(weavy_jit_active))]
{
0
}
#[cfg(weavy_jit_active)]
{
self.native.stencil_count()
}
}
}
pub struct RawHostCallChain<I> {
infos: Vec<I>,
#[cfg(weavy_jit_active)]
calls: Vec<HostCallInfo>,
#[cfg(weavy_jit_active)]
native: NativeProgram,
}
unsafe impl<I: Send> Send for RawHostCallChain<I> {}
unsafe impl<I: Sync> Sync for RawHostCallChain<I> {}
impl<I> RawHostCallChain<I> {
#[must_use]
pub fn new(
infos: Vec<I>,
call: unsafe extern "C" fn(cx: *mut (), info: *const ()) -> bool,
) -> Self {
#[cfg(not(weavy_jit_active))]
{
let _ = call;
Self { infos }
}
#[cfg(weavy_jit_active)]
{
let calls: Vec<_> = infos
.iter()
.map(|info| HostCallInfo {
info: core::ptr::from_ref(info).cast(),
call,
})
.collect();
let mut layout = StencilLayout::new();
let root = layout.start_chain();
let mut previous = None;
for call in &calls {
let current = layout.emit_hostcall(root, core::ptr::from_ref(call));
if let Some(previous) = previous {
layout.patch_hostcall_continuation(previous, current);
}
previous = Some(current);
}
let done = layout.emit_done();
if let Some(previous) = previous {
layout.patch_hostcall_continuation(previous, done);
}
Self {
infos,
calls,
native: NativeProgram::new(layout, root),
}
}
}
pub unsafe fn run<C>(&self, cx: &mut C) -> Result<(), NativeUnavailable> {
#[cfg(not(weavy_jit_active))]
{
let _ = cx;
Err(NativeUnavailable)
}
#[cfg(weavy_jit_active)]
{
let mut host_ctx = HostCallCtx::new(self.native.entry_prog(), cx);
let entry = unsafe { self.native.entry_fn::<HostCallCtx<C>>() };
unsafe {
entry(&mut host_ctx);
}
Ok(())
}
}
#[must_use]
pub fn infos(&self) -> &[I] {
&self.infos
}
#[must_use]
pub fn hostcall_site_count(&self) -> usize {
HostCallChainLayout::for_hostcall_sites(self.infos.len()).hostcall_sites
}
#[must_use]
pub fn hostcall_count(&self) -> usize {
#[cfg(not(weavy_jit_active))]
{
0
}
#[cfg(weavy_jit_active)]
{
self.calls.len()
}
}
#[must_use]
pub fn stencil_count(&self) -> usize {
#[cfg(not(weavy_jit_active))]
{
0
}
#[cfg(weavy_jit_active)]
{
self.native.stencil_count()
}
}
}
#[cfg(weavy_jit_active)]
unsafe extern "C" fn typed_hostcall<C, I>(cx: *mut (), info: *const ()) -> bool
where
I: HostCall<C>,
{
let cx = unsafe { &mut *cx.cast::<C>() };
let info = unsafe { &*info.cast::<I>() };
info.call(cx)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Chain {
pub entry: usize,
pub prog_index: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ProgSlot {
pub prog_index: usize,
pub slot: usize,
}
#[derive(Debug, Default)]
pub struct StencilLayout {
code: Vec<u8>,
progs: Vec<Vec<u64>>,
stencil_count: usize,
}
impl StencilLayout {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn start_chain(&mut self) -> Chain {
let entry = self.code.len();
let prog_index = self.progs.len();
self.progs.push(Vec::new());
Chain { entry, prog_index }
}
pub fn emit_stencil(&mut self, stencil: &[u8]) -> usize {
let start = self.code.len();
self.code.extend_from_slice(stencil);
self.stencil_count += 1;
start
}
#[must_use]
pub fn code_len(&self) -> usize {
self.code.len()
}
pub fn patch_branch26(&mut self, site: usize, target: usize) {
patch_branch26(&mut self.code, site, target);
}
pub fn patch_x86_rel32(&mut self, site: usize, target: usize) {
patch_x86_rel32(&mut self.code, site, target);
}
pub fn patch_continuation(&mut self, site: usize, target: usize) {
#[cfg(all(weavy_jit_active, target_os = "macos", target_arch = "aarch64"))]
{
return self.patch_branch26(site, target);
}
#[cfg(all(weavy_jit_active, target_os = "linux", target_arch = "x86_64"))]
{
return self.patch_x86_rel32(site, target);
}
#[allow(unreachable_code)]
{
let _ = (site, target);
panic!("native copy-and-patch is not available for this target");
}
}
pub fn push_prog_word(&mut self, prog_index: usize, value: u64) {
self.progs[prog_index].push(value);
}
pub fn prog_mut(&mut self, prog_index: usize) -> &mut Vec<u64> {
&mut self.progs[prog_index]
}
pub fn reserve_prog_slot(&mut self, prog_index: usize) -> ProgSlot {
let slot = self.progs[prog_index].len();
self.progs[prog_index].push(0);
ProgSlot { prog_index, slot }
}
pub fn fill_prog_slot(&mut self, slot: ProgSlot, value: u64) {
self.progs[slot.prog_index][slot.slot] = value;
}
pub fn emit_hostcall(&mut self, chain: Chain, info: *const HostCallInfo) -> usize {
self.push_prog_word(chain.prog_index, info as u64);
self.emit_stencil(stencils::HOSTCALL)
}
pub fn emit_done(&mut self) -> usize {
self.emit_stencil(stencils::DONE)
}
pub fn patch_hostcall_continuation(&mut self, hostcall_start: usize, target: usize) {
for &rel in stencils::HOSTCALL_CONT {
self.patch_continuation(hostcall_start + rel, target);
}
}
#[must_use]
pub fn prog(&self, prog_index: usize) -> &[u64] {
&self.progs[prog_index]
}
#[must_use]
pub fn code(&self) -> &[u8] {
&self.code
}
#[must_use]
pub fn stencil_count(&self) -> usize {
self.stencil_count
}
#[must_use]
pub fn into_parts(self) -> (Vec<u8>, Vec<Vec<u64>>, usize) {
(self.code, self.progs, self.stencil_count)
}
}
pub struct NativeProgram {
#[cfg(weavy_jit_active)]
buf: ExecBuf,
progs: Vec<Vec<u64>>,
entry: Chain,
stencil_count: usize,
}
impl NativeProgram {
#[must_use]
#[inline]
pub fn new(layout: StencilLayout, entry: Chain) -> Self {
#[cfg(weavy_jit_active)]
{
let (code, progs, stencil_count) = layout.into_parts();
Self {
buf: ExecBuf::new(&code),
progs,
entry,
stencil_count,
}
}
#[cfg(not(weavy_jit_active))]
{
let (_code, progs, stencil_count) = layout.into_parts();
Self {
progs,
entry,
stencil_count,
}
}
}
#[must_use]
#[inline]
pub fn code_ptr(&self) -> *const u8 {
#[cfg(weavy_jit_active)]
{
self.buf.as_ptr()
}
#[cfg(not(weavy_jit_active))]
{
core::ptr::null()
}
}
#[must_use]
#[inline]
pub unsafe fn entry_fn<C>(&self) -> unsafe extern "C" fn(*mut C) {
unsafe { self.chain_fn(self.entry.entry) }
}
#[must_use]
#[inline]
pub unsafe fn chain_fn<C>(&self, entry: usize) -> unsafe extern "C" fn(*mut C) {
#[cfg(weavy_jit_active)]
{
unsafe {
core::mem::transmute::<*const u8, unsafe extern "C" fn(*mut C)>(
self.code_ptr().add(entry),
)
}
}
#[cfg(not(weavy_jit_active))]
{
let _ = entry;
unsafe extern "C" fn unavailable<C>(_: *mut C) {
unreachable!("native copy-and-patch is unavailable on this target")
}
unavailable::<C>
}
}
#[must_use]
#[inline]
pub fn entry_prog_index(&self) -> usize {
self.entry.prog_index
}
#[must_use]
#[inline]
pub fn entry_prog(&self) -> *const u64 {
self.prog_ptr(self.entry_prog_index())
}
#[must_use]
#[inline]
pub fn prog_ptr(&self, prog_index: usize) -> *const u64 {
self.progs[prog_index].as_ptr()
}
#[inline]
pub fn fill_prog_word(&mut self, prog_index: usize, slot: usize, value: u64) {
self.progs[prog_index][slot] = value;
}
#[inline]
pub fn fill_prog_slot(&mut self, slot: ProgSlot, value: u64) {
self.fill_prog_word(slot.prog_index, slot.slot, value);
}
#[must_use]
#[inline]
pub fn chain_count(&self) -> usize {
self.progs.len()
}
#[must_use]
#[inline]
pub fn stencil_count(&self) -> usize {
self.stencil_count
}
#[must_use]
#[inline]
pub fn prog_slot_count(&self) -> usize {
self.progs.iter().map(Vec::len).sum()
}
}
#[cfg(test)]
mod tests {
use super::StencilLayout;
#[test]
fn layout_tracks_chains_stencils_and_program_slots() {
let mut layout = StencilLayout::new();
let root = layout.start_chain();
let first = layout.emit_stencil(&[1, 2, 3, 4]);
layout.push_prog_word(root.prog_index, 7);
let slot = layout.reserve_prog_slot(root.prog_index);
layout.fill_prog_slot(slot, 11);
let child = layout.start_chain();
let second = layout.emit_stencil(&[5, 6]);
assert_eq!(root.entry, 0);
assert_eq!(root.prog_index, 0);
assert_eq!(first, 0);
assert_eq!(child.entry, 4);
assert_eq!(child.prog_index, 1);
assert_eq!(second, 4);
assert_eq!(layout.code(), &[1, 2, 3, 4, 5, 6]);
assert_eq!(layout.prog(root.prog_index), &[7, 11]);
assert_eq!(layout.stencil_count(), 2);
}
#[cfg(weavy_jit_active)]
fn ret_stencil() -> &'static [u8] {
#[cfg(all(weavy_jit_active, target_os = "macos", target_arch = "aarch64"))]
{
&[0xc0, 0x03, 0x5f, 0xd6]
}
#[cfg(all(weavy_jit_active, target_os = "linux", target_arch = "x86_64"))]
{
&[0xc3]
}
}
#[cfg(weavy_jit_active)]
#[test]
fn native_program_owns_executable_code_and_program_slots() {
use super::NativeProgram;
let mut layout = StencilLayout::new();
let root = layout.start_chain();
layout.emit_stencil(ret_stencil());
layout.push_prog_word(root.prog_index, 7);
let slot = layout.reserve_prog_slot(root.prog_index);
let mut native = NativeProgram::new(layout, root);
native.fill_prog_slot(slot, 11);
assert_eq!(native.chain_count(), 1);
assert_eq!(native.stencil_count(), 1);
assert_eq!(native.prog_slot_count(), 2);
assert!(!native.entry_prog().is_null());
let entry = unsafe { native.entry_fn::<u8>() };
let mut ctx = 0u8;
unsafe { entry(&mut ctx) };
}
#[cfg(weavy_jit_active)]
#[test]
fn shared_hostcall_stencil_runs_consumer_intrinsic() {
use super::{HostCallCtx, HostCallInfo, NativeProgram};
struct State {
value: u64,
}
struct Info {
add: u64,
}
unsafe extern "C" fn add(cx: *mut (), info: *const ()) -> bool {
let state = unsafe { &mut *cx.cast::<State>() };
let info = unsafe { &*info.cast::<Info>() };
state.value += info.add;
true
}
let infos = [Info { add: 41 }];
let calls = [HostCallInfo {
info: core::ptr::from_ref(&infos[0]).cast(),
call: add,
}];
let mut layout = StencilLayout::new();
let root = layout.start_chain();
let hostcall = layout.emit_hostcall(root, core::ptr::from_ref(&calls[0]));
let done = layout.emit_done();
layout.patch_hostcall_continuation(hostcall, done);
let native = NativeProgram::new(layout, root);
let mut state = State { value: 1 };
let mut cx = HostCallCtx::new(native.entry_prog(), &mut state);
let entry = unsafe { native.entry_fn::<HostCallCtx<State>>() };
unsafe {
entry(&mut cx);
}
assert_eq!(state.value, 42);
}
#[cfg(weavy_jit_active)]
#[test]
fn typed_hostcall_chain_runs_consumer_intrinsics_without_raw_abi() {
use super::{HostCall, HostCallChain, HostCallChainLayout};
struct State {
value: u64,
}
struct Add(u64);
impl HostCall<State> for Add {
fn call(&self, cx: &mut State) -> bool {
cx.value += self.0;
true
}
}
let mut chain = HostCallChain::new(vec![Add(20), Add(21)]);
assert_eq!(
HostCallChainLayout::for_hostcall_sites(2).copied_stencils,
3
);
assert_eq!(chain.hostcall_site_count(), 2);
assert_eq!(chain.stencil_count(), 3);
assert_eq!(chain.hostcall_count(), 0);
let mut state = State { value: 1 };
chain.run(&mut state).unwrap();
chain.run(&mut state).unwrap();
assert_eq!(state.value, 83);
assert_eq!(chain.infos().len(), 2);
assert_eq!(chain.hostcall_count(), 2);
assert_eq!(chain.stencil_count(), 3);
}
#[cfg(weavy_jit_active)]
#[test]
fn typed_hostcall_chain_stops_when_consumer_returns_false() {
use super::{HostCall, HostCallChain, HostCallChainLayout};
struct State {
value: u64,
}
struct Step {
add: u64,
keep_running: bool,
}
impl HostCall<State> for Step {
fn call(&self, cx: &mut State) -> bool {
cx.value += self.add;
self.keep_running
}
}
let mut chain = HostCallChain::new(vec![
Step {
add: 1,
keep_running: true,
},
Step {
add: 10,
keep_running: false,
},
Step {
add: 100,
keep_running: true,
},
]);
assert_eq!(
HostCallChainLayout::for_hostcall_sites(3).copied_stencils,
4
);
assert_eq!(chain.hostcall_site_count(), 3);
assert_eq!(chain.stencil_count(), 4);
assert_eq!(chain.hostcall_count(), 0);
let mut state = State { value: 0 };
chain.run(&mut state).unwrap();
assert_eq!(state.value, 11);
assert_eq!(chain.hostcall_count(), 3);
assert_eq!(chain.stencil_count(), 4);
}
#[cfg(weavy_jit_active)]
#[test]
fn raw_hostcall_chain_runs_without_rebuilding_call_records() {
use super::{HostCallChainLayout, RawHostCallChain};
struct State {
value: u64,
}
struct Add(u64);
unsafe extern "C" fn add(cx: *mut (), info: *const ()) -> bool {
let cx = unsafe { &mut *cx.cast::<State>() };
let info = unsafe { &*info.cast::<Add>() };
cx.value += info.0;
true
}
let chain = RawHostCallChain::new(vec![Add(20), Add(21)], add);
assert_eq!(
HostCallChainLayout::for_hostcall_sites(2).copied_stencils,
3
);
assert_eq!(chain.hostcall_site_count(), 2);
assert_eq!(chain.hostcall_count(), 2);
assert_eq!(chain.stencil_count(), 3);
let mut state = State { value: 1 };
unsafe {
chain.run(&mut state).unwrap();
chain.run(&mut state).unwrap();
}
assert_eq!(state.value, 83);
assert_eq!(chain.infos().len(), 2);
assert_eq!(chain.hostcall_count(), 2);
assert_eq!(chain.stencil_count(), 3);
}
#[cfg(not(weavy_jit_active))]
#[test]
fn hostcall_chain_reports_native_unavailable_when_inactive() {
use super::{HostCall, HostCallChain, NativeUnavailable};
struct Add(i64);
impl HostCall<i64> for Add {
fn call(&self, cx: &mut i64) -> bool {
*cx += self.0;
true
}
}
let mut chain = HostCallChain::new(vec![Add(1)]);
let mut value = 0;
assert_eq!(chain.run(&mut value), Err(NativeUnavailable));
assert_eq!(value, 0);
}
#[cfg(not(weavy_jit_active))]
#[test]
fn raw_hostcall_chain_reports_native_unavailable_when_inactive() {
use super::{NativeUnavailable, RawHostCallChain};
unsafe extern "C" fn add(_: *mut (), _: *const ()) -> bool {
true
}
let chain = RawHostCallChain::new(vec![()], add);
let mut value = 0;
assert_eq!(unsafe { chain.run(&mut value) }, Err(NativeUnavailable));
assert_eq!(value, 0);
}
}