1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use crate::detail::{align_down, mut_offset};
use crate::stack::Stack;

// first argument is task handle, second is thunk ptr
pub type InitFn = extern "sysv64" fn(usize, *mut usize) -> !;

pub extern "sysv64" fn gen_init(a1: usize, a2: *mut usize) -> ! {
    super::gen::gen_init_impl(a1, a2)
}

cfg_if::cfg_if! {
    if #[cfg(target_os = "macos")] {
        std::arch::global_asm!(include_str!("asm/asm_x86_64_sysv_macho.S"));
    } else {
        std::arch::global_asm!(include_str!("asm/asm_x86_64_sysv_elf.S"));
    }
}

// #[cfg(not(nightly))]
//#[link(name = "asm", kind = "static")]
extern "sysv64" {
    pub fn bootstrap_green_task();
    pub fn prefetch(data: *const usize);
    pub fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers);
}

/*
#[cfg(nightly)]
mod asm_impl {
    use super::Registers;
    /// prefetch data
    #[inline]
    pub unsafe extern "C" fn prefetch(data: *const usize) {
        llvm_asm!(
            "prefetcht1 $0"
            : // no output
            : "m"(*data)
            :
            : "volatile"
        );
    }

    #[naked]
    #[inline(never)]
    pub unsafe extern "C" fn bootstrap_green_task() {
        llvm_asm!(
            "
                mov %r12, %rdi     // setup the function arg
                mov %r13, %rsi     // setup the function arg
                and $$-16, %rsp    // align the stack pointer
                mov %r14, (%rsp)   // this is the new return address
            "
            : // no output
            : // no input
            : "memory"
            : "volatile"
        );
    }

    #[naked]
    #[inline(never)]
    pub unsafe extern "C" fn swap_registers(out_regs: *mut Registers, in_regs: *const Registers) {
        // The first argument is in %rdi, and the second one is in %rsi
        llvm_asm!(
            ""
            :
            : "{rdi}"(out_regs), "{rsi}"(in_regs)
            :
            :
        );

        // introduce this function to workaround rustc bug! (#6)
        #[naked]
        unsafe extern "C" fn _swap_reg() {
            // Save registers
            llvm_asm!(
                "
                    mov %rbx, (0*8)(%rdi)
                    mov %rsp, (1*8)(%rdi)
                    mov %rbp, (2*8)(%rdi)
                    mov %r12, (4*8)(%rdi)
                    mov %r13, (5*8)(%rdi)
                    mov %r14, (6*8)(%rdi)
                    mov %r15, (7*8)(%rdi)

                    mov (0*8)(%rsi), %rbx
                    mov (1*8)(%rsi), %rsp
                    mov (2*8)(%rsi), %rbp
                    mov (4*8)(%rsi), %r12
                    mov (5*8)(%rsi), %r13
                    mov (6*8)(%rsi), %r14
                    mov (7*8)(%rsi), %r15
                "
                :
                : //"{rdi}"(out_regs), "{rsi}"(in_regs)
                : "memory"
                : "volatile"
            );
        }

        _swap_reg()
    }
}
#[cfg(nightly)]
pub use self::asm_impl::*;
*/

#[repr(C)]
#[derive(Debug)]
pub struct Registers {
    gpr: [usize; 8],
}

impl Registers {
    pub fn new() -> Registers {
        Registers { gpr: [0; 8] }
    }

    #[inline]
    pub fn prefetch(&self) {
        let ptr = self.gpr[1] as *const usize;
        unsafe {
            prefetch(ptr); // RSP
            prefetch(ptr.add(8)); // RSP + 8
        }
    }
}

pub fn initialize_call_frame(
    regs: &mut Registers,
    fptr: InitFn,
    arg: usize,
    arg2: *mut usize,
    stack: &Stack,
) {
    // Redefinitions from rt/arch/x86_64/regs.h
    const RUSTRT_RSP: usize = 1;
    const RUSTRT_RBP: usize = 2;
    const RUSTRT_R12: usize = 4;
    const RUSTRT_R13: usize = 5;
    const RUSTRT_R14: usize = 6;

    let sp = align_down(stack.end());

    // These registers are frobbed by bootstrap_green_task into the right
    // location so we can invoke the "real init function", `fptr`.
    regs.gpr[RUSTRT_R12] = arg;
    regs.gpr[RUSTRT_R13] = arg2 as usize;
    regs.gpr[RUSTRT_R14] = fptr as usize;

    // Last base pointer on the stack should be 0
    regs.gpr[RUSTRT_RBP] = 0;

    // setup the init stack
    // this is prepared for the swap context
    regs.gpr[RUSTRT_RSP] = mut_offset(sp, -2) as usize;

    unsafe {
        // leave enough space for RET
        *mut_offset(sp, -2) = bootstrap_green_task as usize;
        *mut_offset(sp, -1) = 0;
    }
}