summary refs log tree commit diff
path: root/src/rt/arch/x86_64/_context.S
blob: 36caf7720c40c5ece1a138bd524c86884fb0364b (plain)
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// Mark stack as non-executable
#if defined(__linux__) && defined(__ELF__)
.section	.note.GNU-stack, "", @progbits
#endif

#include "regs.h"
#define ARG0 RUSTRT_ARG0_S
#define ARG1 RUSTRT_ARG1_S

        .text

/*
According to ABI documentation found at
http://www.x86-64.org/documentation.html
and Microsoft discussion at
http://msdn.microsoft.com/en-US/library/9z1stfyw%28v=VS.80%29.aspx.

BOTH CALLING CONVENTIONS

Callee save registers:
	R12--R15, RDI, RSI, RBX, RBP, RSP
        XMM0--XMM5

Caller save registers:
	RAX, RCX, RDX, R8--R11
        XMM6--XMM15
        Floating point stack

MAC/AMD CALLING CONVENTIONS

Integer arguments go in registers:
        rdi, rsi, rdx, rcx, r8, r9

User flags have no specified role and are not preserved
        across calls, with the exception of DF in %rFLAGS,
        which must be clear (set to "forward" direction)
        on function entry and return.

MICROSOFT CALLING CONVENTIONS

Return value: RAX

First four arguments:
        RCX, RDX, R8, R9
        XMM0, XMM1, XMM2, XMM3
*/

/*
        Stores current registers into arg0/RCX and restores
        registers found in arg1/RDX. This is used by our
	    implementation of getcontext.  Only saves/restores nonvolatile
        registers and the register used for the first argument.
        Volatile registers in general ought to be saved by the caller
        anyhow.
*/

#if defined(__APPLE__)
#define SWAP_REGISTERS _rust_swap_registers
#else
#define SWAP_REGISTERS rust_swap_registers
#endif

// swap_registers(registers_t *oregs, registers_t *regs)
.globl SWAP_REGISTERS
SWAP_REGISTERS:
        // n.b. when we enter, the return address is at the top of
        // the stack (i.e., 0(%RSP)) and the argument is in
        // RUSTRT_ARG0_S.  We
        // simply save all NV registers into oregs.
        // We then restore all NV registers from regs.  This restores
        // the old stack pointer, which should include the proper
        // return address. We can therefore just return normally to
        // jump back into the old code.

        // Save instruction pointer:
        pop %rax
        mov %rax, (RUSTRT_IP*8)(RUSTRT_ARG0_S)

        // Save non-volatile integer registers:
        //   (including RSP)
        mov %rbx, (RUSTRT_RBX*8)(ARG0)
        mov %rsp, (RUSTRT_RSP*8)(ARG0)
        mov %rbp, (RUSTRT_RBP*8)(ARG0)
        mov %r12, (RUSTRT_R12*8)(ARG0)
        mov %r13, (RUSTRT_R13*8)(ARG0)
        mov %r14, (RUSTRT_R14*8)(ARG0)
        mov %r15, (RUSTRT_R15*8)(ARG0)

#if defined(__MINGW32__) || defined(_WINDOWS)
        mov %rdi, (RUSTRT_RDI*8)(ARG0)
        mov %rsi, (RUSTRT_RSI*8)(ARG0)
#endif

        // Save 0th argument register:
        mov ARG0, (RUSTRT_ARG0*8)(ARG0)

        // Save non-volatile XMM registers:
#if defined(__MINGW32__) || defined(_WINDOWS)
        movapd %xmm6, (RUSTRT_XMM6*8)(ARG0)
        movapd %xmm7, (RUSTRT_XMM7*8)(ARG0)
        movapd %xmm8, (RUSTRT_XMM8*8)(ARG0)
        movapd %xmm9, (RUSTRT_XMM9*8)(ARG0)
        movapd %xmm10, (RUSTRT_XMM10*8)(ARG0)
        movapd %xmm11, (RUSTRT_XMM11*8)(ARG0)
        movapd %xmm12, (RUSTRT_XMM12*8)(ARG0)
        movapd %xmm13, (RUSTRT_XMM13*8)(ARG0)
        movapd %xmm14, (RUSTRT_XMM14*8)(ARG0)
        movapd %xmm15, (RUSTRT_XMM15*8)(ARG0)
#else
        movapd %xmm0, (RUSTRT_XMM0*8)(ARG0)
        movapd %xmm1, (RUSTRT_XMM1*8)(ARG0)
        movapd %xmm2, (RUSTRT_XMM2*8)(ARG0)
        movapd %xmm3, (RUSTRT_XMM3*8)(ARG0)
        movapd %xmm4, (RUSTRT_XMM4*8)(ARG0)
        movapd %xmm5, (RUSTRT_XMM5*8)(ARG0)
#endif

        // Restore non-volatile integer registers:
        //   (including RSP)
        mov (RUSTRT_RBX*8)(ARG1), %rbx
        mov (RUSTRT_RSP*8)(ARG1), %rsp
        mov (RUSTRT_RBP*8)(ARG1), %rbp
        mov (RUSTRT_R12*8)(ARG1), %r12
        mov (RUSTRT_R13*8)(ARG1), %r13
        mov (RUSTRT_R14*8)(ARG1), %r14
        mov (RUSTRT_R15*8)(ARG1), %r15

#if defined(__MINGW32__) || defined(_WINDOWS)
        mov (RUSTRT_RDI*8)(ARG1), %rdi
        mov (RUSTRT_RSI*8)(ARG1), %rsi
#endif

        // Restore 0th argument register:
        mov (RUSTRT_ARG0*8)(ARG1), ARG0

        // Restore non-volatile XMM registers:
#if defined(__MINGW32__) || defined(_WINDOWS)
        movapd (RUSTRT_XMM6*8)(ARG1), %xmm6
        movapd (RUSTRT_XMM7*8)(ARG1), %xmm7
        movapd (RUSTRT_XMM8*8)(ARG1), %xmm8
        movapd (RUSTRT_XMM9*8)(ARG1), %xmm9
        movapd (RUSTRT_XMM10*8)(ARG1), %xmm10
        movapd (RUSTRT_XMM11*8)(ARG1), %xmm11
        movapd (RUSTRT_XMM12*8)(ARG1), %xmm12
        movapd (RUSTRT_XMM13*8)(ARG1), %xmm13
        movapd (RUSTRT_XMM14*8)(ARG1), %xmm14
        movapd (RUSTRT_XMM15*8)(ARG1), %xmm15
#else
        movapd (RUSTRT_XMM0*8)(ARG1), %xmm0
        movapd (RUSTRT_XMM1*8)(ARG1), %xmm1
        movapd (RUSTRT_XMM2*8)(ARG1), %xmm2
        movapd (RUSTRT_XMM3*8)(ARG1), %xmm3
        movapd (RUSTRT_XMM4*8)(ARG1), %xmm4
        movapd (RUSTRT_XMM5*8)(ARG1), %xmm5
#endif

        // Jump to the instruction pointer
        // found in regs:
        jmp *(RUSTRT_IP*8)(ARG1)

// This function below, rust_bootstrap_green_task, is used to initialize a green
// task. This code is the very first code that is run whenever a green task
// starts. The only assumptions that this code makes is that it has a register
// context previously set up by Context::new() and some values are in some
// special registers.
//
// In theory the register context could be set up and then the context switching
// would plop us directly into some 'extern "C" fn', but not all platforms have
// the argument registers saved throughout a context switch (linux doesn't save
// rdi/rsi, the first two argument registers). Instead of modifying all context
// switches, instead the initial data for starting a green thread is shoved into
// unrelated registers (r12/13, etc) which always need to be saved on context
// switches anyway.
//
// With this strategy we get the benefit of being able to pass a fair bit of
// contextual data from the start of a green task to its init function, as well
// as not hindering any context switches.
//
// If you alter this code in any way, you likely need to update
// src/libgreen/context.rs as well.

#if defined(__APPLE__)
#define BOOTSTRAP _rust_bootstrap_green_task
#else
#define BOOTSTRAP rust_bootstrap_green_task
#endif
.globl BOOTSTRAP
BOOTSTRAP:
    mov %r12, RUSTRT_ARG0_S
    mov %r13, RUSTRT_ARG1_S
    mov %r14, RUSTRT_ARG2_S
    jmpq *%r15