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;
pub const STACK_ALIGNMENT: usize = 16;
pub const PARENT_STACK_OFFSET: usize = 16;
pub const PARENT_LINK_OFFSET: usize = 16;
pub type StackWord = u64;
global_asm!(
".balign 4",
asm_function_begin!("stack_init_trampoline"),
".cfi_startproc",
cfi_signal_frame!(),
"addi.d $sp, $sp, -32",
"st.d $s8, $sp, 16",
"st.d $ra, $sp, 8",
"st.d $fp, $sp, 0",
"addi.d $t0, $sp, 16",
"st.d $t0, $a1, -16",
"move $fp, $a1",
"addi.d $a1, $a1, -16",
"addi.d $a2, $a2, 32",
"move $sp, $a2",
".cfi_escape 0x0f, 5, 0x86, 0x70, 0x06, 0x23, 0x10",
".cfi_offset 31, -16",
".cfi_offset 1, -24",
".cfi_offset 22, -32",
"la.pcrel $ra, 0f",
"ld.d $t0, $a1, 8",
"jr $t0",
"0:",
asm_function_alt_entry!("stack_init_trampoline_return"),
"break 0",
".cfi_endproc",
asm_function_end!("stack_init_trampoline"),
);
global_asm!(
".balign 4",
asm_function_begin!("stack_call_trampoline"),
".cfi_startproc",
cfi_signal_frame!(),
"addi.d $sp, $sp, -16",
".cfi_def_cfa_offset 16",
"st.d $ra, $sp, 8",
"st.d $fp, $sp, 0",
"addi.d $fp, $sp, 16",
".cfi_def_cfa 22, 0",
".cfi_offset 1, -8",
".cfi_offset 22, -16",
"move $sp, $a1",
"jirl $ra, $a2, 0",
"addi.d $sp, $fp, -16",
"ld.d $ra, $sp, 8",
"ld.d $fp, $sp, 0",
"addi.d $sp, $sp, 16",
"jr $ra",
".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, 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!(),
"ld.d $t0, $a2, 16",
"jirl $ra, $t0, 0",
"addi.d $sp, $a2, 16",
inlateout("$a0") arg => ret_val,
lateout("$a1") ret_sp,
in("$a1") stack_base.get() as u64,
in("$a2") sp.get() as u64,
lateout("$s0") _, lateout("$s1") _, lateout("$s2") _, lateout("$s3") _,
lateout("$s4") _, lateout("$s5") _, lateout("$s6") _, lateout("$s7") _,
lateout("$fs0") _, lateout("$fs1") _, lateout("$fs2") _, lateout("$fs3") _,
lateout("$fs4") _, lateout("$fs5") _, lateout("$fs6") _, lateout("$fs7") _,
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.d $sp, $sp, -32",
"st.d $fp, $sp, 0",
"st.d $s8, $sp, 8",
"la.pcrel $ra, 0f",
"st.d $ra, $sp, 16",
"ld.d $a2, $t0, 0",
"move $a1, $sp",
"ld.d $s8, $a2, 0",
"ld.d $ra, $a2, -8",
"ld.d $fp, $a2, -16",
cfi_reset_args_size_yield!(),
"jr $ra",
"0:",
"addi.d $sp, $sp, -32",
"st.d $s8, $sp, 16",
"st.d $ra, $sp, 8",
"st.d $fp, $sp, 0",
"addi.d $t0, $sp, 16",
"st.d $t0, $a1, -16",
"ld.d $s8, $a2, 8",
"ld.d $fp, $a2, 0",
"addi.d $sp, $a2, 32",
inlateout("$a0") arg => ret_val,
in("$t0") parent_link as u64,
lateout("$s0") _, lateout("$s1") _, lateout("$s2") _, lateout("$s3") _,
lateout("$s4") _, lateout("$s5") _, lateout("$s6") _, lateout("$s7") _,
lateout("$fs0") _, lateout("$fs1") _, lateout("$fs2") _, lateout("$fs3") _,
lateout("$fs4") _, lateout("$fs5") _, lateout("$fs6") _, lateout("$fs7") _,
clobber_abi("C"),
);
ret_val
}
#[inline(always)]
pub unsafe fn switch_and_reset(arg: EncodedValue, parent_link: *mut StackPointer) -> ! {
asm!(
"ld.d $a2, {parent_link}, 0",
"ld.d $s8, $a2, 0",
"ld.d $ra, $a2, -8",
"ld.d $fp, $a2, -16",
"jr $ra",
parent_link = in(reg) parent_link as u64,
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!(
"la.pcrel $ra, 0f",
"addi.d $sp, $sp, -32"
"st.d $s8, $sp, 16"
"st.d $ra, $sp, 8"
"st.d $fp, $sp, 0"
"addi.d $t1, $sp, 16"
"st.d $t1, $a1, -16"
"ld.d $ra, $t0, 16"
"ld.d $s8, $t0, 8"
"ld.d $fp, $t0, 0"
"addi.d $sp, $t0, 32",
cfi_reset_args_size_root!(),
"b {throw}",
"0:",
"addi.d $sp, $a2, 32",
throw = sym throw,
in("$a0") forced_unwind.0.get(),
lateout("$a0") ret_val,
lateout("$a1") ret_sp,
in("$a1") stack_base.get() as u64,
in("$t0") sp.get() as u64,
lateout("$s0") _, lateout("$s1") _, lateout("$s2") _, lateout("$s3") _,
lateout("$s4") _, lateout("$s5") _, lateout("$s6") _, lateout("$s7") _,
lateout("$fs0") _, lateout("$fs1") _, lateout("$fs2") _, lateout("$fs3") _,
lateout("$fs4") _, lateout("$fs5") _, lateout("$fs6") _, lateout("$fs7") _,
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(32);
drop_fn(ptr);
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct TrapHandlerRegs {
pub pc: u64,
pub sp: u64,
pub a0: u64,
pub a1: u64,
pub fp: u64,
pub ra: u64,
}
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, 16, val);
let val_ptr = sp;
TrapHandlerRegs {
pc: handler as u64,
sp: sp as u64,
a0: val_ptr as u64,
a1: parent_link as u64,
fp: stack_base.get() as u64,
ra: stack_init_trampoline_return.as_ptr() as u64,
}
}
#[inline]
pub unsafe fn on_stack(arg: *mut u8, stack: impl Stack, f: StackCallFunc) {
asm_may_unwind_root!(
cfi_reset_args_size_root!(),
concat!("bl ", asm_mangle!("stack_call_trampoline")),
"nop",
in("$a0") arg,
in("$a1") adjusted_stack_base(&stack).get(),
in("$a2") f,
clobber_abi("C"),
);
}