summary refs log tree commit diff
path: root/src/rt/arch/x86_64/morestack.S
blob: 4acb50497d92e44d04935d2d09226f6c4a4f6d2f (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
/*
	__morestack

	See i386/morestack.S for the lengthy, general explanation.
*/

.text

#if defined(__APPLE__) || defined(_WIN32)
#define UPCALL_NEW_STACK        _upcall_new_stack
#define UPCALL_DEL_STACK        _upcall_del_stack
#define MORESTACK               ___morestack
#else
#define UPCALL_NEW_STACK        upcall_new_stack
#define UPCALL_DEL_STACK        upcall_del_stack
#define MORESTACK               __morestack
#endif

.globl UPCALL_NEW_STACK
.globl UPCALL_DEL_STACK
.globl MORESTACK

#if defined(__linux__) || defined(__FreeBSD__)
	.hidden MORESTACK
#else
#if defined(__APPLE__)
	.private_extern MORESTACK
#endif
#endif

#ifdef __ELF__
	.type MORESTACK,@function
#endif


#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
MORESTACK:
	.cfi_startproc

	pushq %rbp
	// The CFA is 24 bytes above the register that it will
	// be associated with for this frame (%rbp). That is 8
	// bytes greater than a normal frame, to allow the unwinder
	// to skip the partial frame of the original function.
	.cfi_def_cfa_offset 24
	// %rbp is -24 bytes from the CFA
	.cfi_offset %rbp, -24
	movq %rsp, %rbp
	// Calculate the CFA as on offset from %ebp
	.cfi_def_cfa_register %rbp

        subq $184, %rsp

	// Save argument registers of the original function
	movq %rdi,       (%rsp)
	movq %rsi,      8(%rsp)
	movq %rdx,     16(%rsp)
	movq %rcx,     24(%rsp)
	movq %r8,      32(%rsp)
	movq %r9,      40(%rsp)
	movdqa %xmm0,  48(%rsp)
	movdqa %xmm1,  64(%rsp)
	movdqa %xmm2,  80(%rsp)
	movdqa %xmm3,  96(%rsp)
	movdqa %xmm4, 112(%rsp)
	movdqa %xmm5, 128(%rsp)
	movdqa %xmm6, 144(%rsp)
	movdqa %xmm7, 160(%rsp)

	// Calculate the address of the stack arguments.
	// We have the base pointer, __morestack's return address,
	// and __morestack's caller's return address to skip
	movq %rbp, %rax
	addq $24, %rax  // Base pointer, return address x2

	// The arguments to __morestack are passed in %r10 & %r11

	movq %r11, %rdx // Size of stack arguments
	movq %rax, %rsi // Address of stack arguments
	movq %r10, %rdi // The amount of stack needed
        
#ifdef __APPLE__
	call UPCALL_NEW_STACK
#endif
#ifdef __linux__
	call UPCALL_NEW_STACK@PLT
#endif
#ifdef __FreeBSD__
	call UPCALL_NEW_STACK@PLT
#endif

	// Pop the saved arguments
	movq      (%rsp), %rdi
	movq     8(%rsp), %rsi
	movq    16(%rsp), %rdx
	movq    24(%rsp), %rcx
	movq    32(%rsp), %r8
	movq    40(%rsp), %r9
	movdqa  48(%rsp), %xmm0
	movdqa  64(%rsp), %xmm1
	movdqa  80(%rsp), %xmm2
	movdqa  96(%rsp), %xmm3
	movdqa 112(%rsp), %xmm4
	movdqa 128(%rsp), %xmm5
	movdqa 144(%rsp), %xmm6
	movdqa 160(%rsp), %xmm7

	addq $184, %rsp

        movq 8(%rbp),%r10       // Grab the return pointer.
        incq %r10               // Skip past the `ret` in our parent frame
        movq %rax,%rsp          // Switch to the new stack.

        call *%r10              // Reenter the caller function

	// Switch back to the rust stack
	movq %rbp, %rsp

	// Save the return value
	pushq %rax

#ifdef __APPLE__
	call UPCALL_DEL_STACK
#endif
#ifdef __linux__
	call UPCALL_DEL_STACK@PLT
#endif
#ifdef __FreeBSD__
	call UPCALL_DEL_STACK@PLT
#endif

	popq %rax // Restore the return value
	popq %rbp
	ret
	
	.cfi_endproc

#else
MORESTACK:
	ret
#endif