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
164
165
166
167
168
169
170
171
172
173
174
175
//! This is a wrapper crate around all other context switch implementation crates
//! that helps to manage the complex configuration options involving SIMD and personalities.

#![no_std]
#![feature(naked_functions)]

pub use context_switch_regular::read_first_register;

// If `simd_personality` is enabled, all of the `context_switch*` implementation crates are simultaneously enabled,
// in order to allow choosing one of them based on the configuration options of each Task (SIMD, regular, etc).
// If `simd_personality` is NOT enabled, then we use the context_switch routine that matches the actual build target. 
cfg_if::cfg_if! {
    if #[cfg(simd_personality)] {
        use core::arch::asm;
        pub use context_switch_sse::*;
        pub use context_switch_regular::*;
        pub use context_switch_avx::*;

        /// Switches context from a regular Task to an SSE Task.
        /// 
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        /// 
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks. 
        #[naked]
        pub unsafe extern "C" fn context_switch_regular_to_sse(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                switch_stacks!(),
                restore_registers_sse!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }


        /// Switches context from an SSE Task to a regular Task.
        ///
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        ///
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks.
        #[naked]
        pub unsafe extern "C" fn context_switch_sse_to_regular(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                save_registers_sse!(),
                switch_stacks!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }

        /// Switches context from a regular Task to an AVX Task.
        /// 
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        /// 
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks. 
        #[naked]
        pub unsafe extern "C" fn context_switch_regular_to_avx(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                switch_stacks!(),
                restore_registers_avx!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }

        /// Switches context from an SSE Task to an AVX regular Task.
        /// 
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        /// 
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks. 
        #[naked]
        pub unsafe extern "C" fn context_switch_sse_to_avx(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                save_registers_sse!(),
                switch_stacks!(),
                restore_registers_avx!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }

        /// Switches context from an AVX Task to a regular Task.
        /// 
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        /// 
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks. 
        #[naked]
        pub unsafe extern "C" fn context_switch_avx_to_regular(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                save_registers_avx!(),
                switch_stacks!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }

        /// Switches context from an AVX Task to an SSE Task.
        /// 
        /// # Arguments
        /// * First argument  (in `RDI`): mutable pointer to the previous task's stack pointer
        /// * Second argument (in `RSI`): the value of the next task's stack pointer
        /// 
        /// # Safety
        /// This function is unsafe because it changes the content on both task's stacks. 
        #[naked]
        pub unsafe extern "C" fn context_switch_avx_to_sse(_prev_stack_pointer: *mut usize, _next_stack_pointer_value: usize) {
            // Since this is a naked function that expects its arguments in two registers,
            // you CANNOT place any log statements or other instructions here
            // before, in between, or after anything below.
            asm!(
                save_registers_regular!(),
                save_registers_avx!(),
                switch_stacks!(),
                restore_registers_sse!(),
                restore_registers_regular!(),
                options(noreturn)
            );
        }
    }

    // BELOW HERE: simd_personality is disabled, and we just need to re-export a single Context & context_switch.
    // Because "else-if" statements are executed top-down, we need to put newer standards (supersets) before older standards (subsets).
    // For example, AVX512 is above AVX2, which is above AVX, which is above SSE, which is above regular (non-SIMD).

    else if #[cfg(target_feature = "avx")] {
        pub use context_switch_avx::ContextAVX as Context;
        pub use context_switch_avx::context_switch_avx as context_switch;
    }

    else if #[cfg(target_feature = "sse2")] {
        // this crate covers SSE, SSE2, SSE3, SSE4, but we're only currently using it for SSE2
        pub use context_switch_sse::ContextSSE as Context;
        pub use context_switch_sse::context_switch_sse as context_switch;
    }

    else {
        // this covers only the default x86_64 registers
        pub use context_switch_regular::ContextRegular as Context;
        pub use context_switch_regular::context_switch_regular as context_switch;
    }
}