use core::arch::{asm, global_asm};
use super::{allocate_obj_on_stack, push};
use crate::coroutine::adjusted_stack_base;
use crate::stack::{Stack, StackPointer};
use crate::unwind::{
asm_may_unwind_root, asm_may_unwind_yield, cfi_reset_args_size_root, cfi_reset_args_size_yield,
InitialFunc, StackCallFunc, TrapHandler,
};
use crate::util::EncodedValue;
cfg_if::cfg_if! {
if #[cfg(target_pointer_width = "64")] {
macro_rules! x {
($val32:expr, $val64:expr) => {
$val64
};
}
} else if #[cfg(target_pointer_width = "32")] {
macro_rules! x {
($val32:expr, $val64:expr) => {
$val32
};
}
}
}
macro_rules! xlen_bytes {
() => {
x!("4", "8")
};
($word_offset:expr) => {
concat!("((", stringify!($word_offset), ") * ", xlen_bytes!(), ")")
};
}
macro_rules! l {
($val:expr, $word_offset:expr, $addr:expr) => {
concat!(
x!("lw", "ld"),
" ",
$val,
", ",
xlen_bytes!($word_offset),
"(",
$addr,
")"
)
};
}
macro_rules! s {
($val:expr, $word_offset:expr, $addr:expr) => {
concat!(
x!("sw", "sd"),
" ",
$val,
", ",
xlen_bytes!($word_offset),
"(",
$addr,
")"
)
};
}
macro_rules! addi {
($dest:expr, $src:expr, $word_offset:expr) => {
concat!("addi ", $dest, ", ", $src, ", ", xlen_bytes!($word_offset),)
};
}
pub const STACK_ALIGNMENT: usize = 16;
pub const PARENT_STACK_OFFSET: usize = x!(8, 16);
pub const PARENT_LINK_OFFSET: usize = x!(8, 16);
pub type StackWord = usize;
global_asm!(
".balign 4",
asm_function_begin!("stack_init_trampoline"),
".cfi_startproc",
cfi_signal_frame!(),
addi!("sp", "sp", -4),
s!("s1", 2, "sp"),
s!("ra", 1, "sp"),
s!("s0", 0, "sp"),
addi!("t0", "sp", 2),
s!("t0", -2, "a1"),
"mv s0, a1",
addi!("a1", "a1", -2),
addi!("a2", "a2", 4),
"mv sp, a2",
concat!(
".cfi_escape 0x0f, 5, 0x78, ",
x!("0x78", "0x70"),
", 0x06, 0x23, ",
xlen_bytes!(2)
),
concat!(".cfi_offset s1, ", xlen_bytes!(-2)),
concat!(".cfi_offset ra, ", xlen_bytes!(-3)),
concat!(".cfi_offset s0, ", xlen_bytes!(-4)),
"lla ra, 0f",
l!("t0", 1, "a1"),
"jr t0",
"0:",
asm_function_alt_entry!("stack_init_trampoline_return"),
"unimp",
".cfi_endproc",
asm_function_end!("stack_init_trampoline"),
);
global_asm!(
".balign 4",
asm_function_begin!("stack_call_trampoline"),
".cfi_startproc",
cfi_signal_frame!(),
"addi sp, sp, -16",
".cfi_def_cfa_offset 16",
s!("ra", 1, "sp"),
s!("s0", 0, "sp"),
addi!("s0", "sp", 2),
concat!(".cfi_def_cfa s0, ", x!("8", "0")),
concat!(".cfi_offset ra, ", x!("-12", "-8")),
concat!(".cfi_offset s0, ", x!("-16", "-16")),
"mv sp, a1",
"jalr a2",
addi!("sp", "s0", -2),
l!("ra", 1, "sp"),
l!("s0", 0, "sp"),
"addi sp, sp, 16",
"ret",
".cfi_endproc",
asm_function_end!("stack_call_trampoline"),
);
extern "C" {
fn stack_init_trampoline(arg: EncodedValue, stack_base: StackPointer, stack_ptr: StackPointer);
static stack_init_trampoline_return: [u8; 0];
#[allow(dead_code)]
fn stack_call_trampoline(arg: *mut u8, sp: StackPointer, f: StackCallFunc);
}
#[inline]
pub unsafe fn init_stack<T>(stack: &impl Stack, func: InitialFunc<T>, obj: T) -> StackPointer {
let mut sp = adjusted_stack_base(stack).get();
push(&mut sp, Some(func as StackWord));
push(&mut sp, None);
allocate_obj_on_stack(&mut sp, x!(8, 16), obj);
debug_assert_eq!(sp % STACK_ALIGNMENT, 0);
push(&mut sp, None);
push(
&mut sp,
Some(stack_init_trampoline as *const () as StackWord),
);
push(&mut sp, None);
push(&mut sp, None);
StackPointer::new_unchecked(sp)
}
#[inline]
pub unsafe fn switch_and_link(
arg: EncodedValue,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
let (ret_val, ret_sp);
asm_may_unwind_root!(
cfi_reset_args_size_root!(),
l!("t0", 2, "a2"),
"jalr t0",
addi!("sp", "a2", 2),
inlateout("a0") arg => ret_val,
lateout("a1") ret_sp,
in("a1") stack_base.get(),
in("a2") sp.get(),
lateout("s2") _, lateout("s3") _, lateout("s4") _, lateout("s5") _,
lateout("s6") _, lateout("s7") _, lateout("s8") _, lateout("s9") _,
lateout("s10") _, lateout("s11") _,
lateout("fs0") _, lateout("fs1") _, lateout("fs2") _, lateout("fs3") _,
lateout("fs4") _, lateout("fs5") _, lateout("fs6") _, lateout("fs7") _,
lateout("fs8") _, lateout("fs9") _, lateout("fs10") _, lateout("fs11") _,
clobber_abi("C"),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline(always)]
pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) -> EncodedValue {
let ret_val;
asm_may_unwind_yield!(
addi!("sp", "sp", -4),
s!("s0", 0, "sp"),
s!("s1", 1, "sp"),
"lla ra, 0f",
s!("ra", 2, "sp"),
l!("a2", 0, "t0"),
"mv a1, sp",
l!("s1", 0, "a2"),
l!("ra", -1, "a2"),
l!("s0", -2, "a2"),
cfi_reset_args_size_yield!(),
"ret",
"0:",
addi!("sp", "sp", -4),
s!("s1", 2, "sp"),
s!("ra", 1, "sp"),
s!("s0", 0, "sp"),
addi!("t0", "sp", 2),
s!("t0", -2, "a1"),
l!("s1", 1, "a2"),
l!("s0", 0, "a2"),
addi!("sp", "a2", 4),
inlateout("a0") arg => ret_val,
in("t0") parent_link,
lateout("s2") _, lateout("s3") _, lateout("s4") _, lateout("s5") _,
lateout("s6") _, lateout("s7") _, lateout("s8") _, lateout("s9") _,
lateout("s10") _, lateout("s11") _,
lateout("fs0") _, lateout("fs1") _, lateout("fs2") _, lateout("fs3") _,
lateout("fs4") _, lateout("fs5") _, lateout("fs6") _, lateout("fs7") _,
lateout("fs8") _, lateout("fs9") _, lateout("fs10") _, lateout("fs11") _,
clobber_abi("C"),
);
ret_val
}
#[inline(always)]
pub unsafe fn switch_and_reset(arg: EncodedValue, parent_link: *mut StackPointer) -> ! {
asm!(
l!("a2", 0, "{parent_link}"),
l!("s1", 0, "a2"),
l!("ra", -1, "a2"),
l!("s0", -2, "a2"),
"ret",
parent_link = in(reg) parent_link,
in("a0") arg,
in("a1") 0,
options(noreturn),
);
}
#[inline]
#[cfg(feature = "asm-unwind")]
pub unsafe fn switch_and_throw(
forced_unwind: crate::unwind::ForcedUnwind,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
extern "C-unwind" fn throw(forced_unwind: crate::unwind::ForcedUnwind) -> ! {
extern crate std;
use std::boxed::Box;
std::panic::resume_unwind(Box::new(forced_unwind));
}
let (ret_val, ret_sp);
asm_may_unwind_root!(
"lla ra, 0f",
addi!("sp", "sp", -4),
s!("s1", 2, "sp"),
s!("ra", 1, "sp"),
s!("s0", 0, "sp"),
addi!("t1", "sp", 2),
s!("t1", -2, "a1"),
l!("ra", 2, "t0"),
l!("s1", 1, "t0"),
l!("s0", 0, "t0"),
addi!("sp", "t0", 4),
"tail {throw}",
"0:",
addi!("sp", "a2", 2),
throw = sym throw,
in("a0") forced_unwind.0.get(),
lateout("a0") ret_val,
lateout("a1") ret_sp,
in("a1") stack_base.get(),
in("t0") sp.get(),
lateout("s2") _, lateout("s3") _, lateout("s4") _, lateout("s5") _,
lateout("s6") _, lateout("s7") _, lateout("s8") _, lateout("s9") _,
lateout("s10") _, lateout("s11") _,
lateout("fs0") _, lateout("fs1") _, lateout("fs2") _, lateout("fs3") _,
lateout("fs4") _, lateout("fs5") _, lateout("fs6") _, lateout("fs7") _,
lateout("fs8") _, lateout("fs9") _, lateout("fs10") _, lateout("fs11") _,
clobber_abi("C"),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline]
pub unsafe fn drop_initial_obj(
_stack_base: StackPointer,
stack_ptr: StackPointer,
drop_fn: unsafe fn(ptr: *mut u8),
) {
let ptr = (stack_ptr.get() as *mut u8).add(x!(16, 32));
drop_fn(ptr);
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct TrapHandlerRegs {
pub pc: usize,
pub ra: usize,
pub sp: usize,
pub a0: usize,
pub a1: usize,
pub s0: usize,
}
pub unsafe fn setup_trap_trampoline<T>(
stack_base: StackPointer,
val: T,
handler: TrapHandler<T>,
) -> TrapHandlerRegs {
let parent_link = stack_base.get() - PARENT_LINK_OFFSET;
let mut sp = parent_link;
allocate_obj_on_stack(&mut sp, x!(8, 16), val);
let val_ptr = sp;
debug_assert_eq!(sp % STACK_ALIGNMENT, 0);
TrapHandlerRegs {
pc: handler as usize,
ra: stack_init_trampoline_return.as_ptr() as usize,
sp,
a0: val_ptr,
a1: parent_link,
s0: stack_base.get(),
}
}
#[inline]
pub unsafe fn on_stack(arg: *mut u8, stack: impl Stack, f: StackCallFunc) {
asm_may_unwind_root!(
cfi_reset_args_size_root!(),
concat!("call ", asm_mangle!("stack_call_trampoline")),
"nop",
in("a0") arg,
in("a1") adjusted_stack_base(&stack).get(),
in("a2") f,
clobber_abi("C"),
)
}