aboutsummaryrefslogtreecommitdiff
path: root/include/gc.s
blob: ae71d5c7824cd8837fcb20f509646afd339f54ce (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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

.ifndef _gc_s_file
_gc_s_file:

.section .bss
_write_region_start:
	# begin of the active memory area
	cell 0
_write_region_end:
	# end of the active memory area (%rsp kinda starts here and goes down
	# towars the start)
	cell 0
_gc_trigger:
	# point in memory where the gc will trigger (we don't necessarily wait for the write region to fill up!)
	cell 0

_gc_last_size:
	# how much data we evacuated last time
	cell 0
_gc_min_alloc:
	# minimum possible allocation
	cell 0 # tunable constant
_gc_grow_ratio:
	# 256th's of the minimal amount of memory increment compared to the
	# last time. New minimal amount is compared as:
	# (ratio * last size) >> 8
	cell 0 # tunable constant
_gc_shrink_ratio:
	# 256th's of the ratio of post-gc still-free to-space that should be considered for discarding
	cell 0

_gc_region_start:
	# in GC, this region is being evacuated and will eventually disappear
	cell 0
_gc_region_end:
	# end of the disappear region
	cell 0

_gc_backup_thunk:
	# backup of %rsi so that we can use the register for other nonsense
	cell 0
_gc_backup_cont:
	# backup of %rbp for same reason
	cell 0

.section .text

.macro needs_alloc amount
	mov %rsp, %rax
	sub _write_region_start, %rax
	cmp \amount, %rax
	jb _uskel_gc
.endm

_uskel_alloc:
	mov %rsi, %r15 # %rsi is the return address; back it up

	# calculate the desired size to %r14
	mov _gc_min_alloc, %r14
	#add _gc_region_end, %r14
	#sub _gc_region_start, %r14

	# check if the desired size isn't greater because of the last gc use
	mov _gc_last_size, %rax
	mulq _gc_grow_ratio
	shr $8, %rax
	add _gc_min_alloc, %rax
	cmp %r14, %rax
	cmova %rax, %r14

	# check if we don't need even more space because we need to evacuate stuff
	mov _gc_region_end, %rax
	sub %rsp, %rax # trick -- if we counted from gc region start, allocated memory could never shrink
	cmp %r14, %rax
	cmova %rax, %r14

	and $0xfffffffffffffff8, %r14 #align

	alloc_goes_mmap:
	mov $9, %rax # mmap
	mov $0, %rdi # addr = NULL
	mov %r14, %rsi # len = %r14
	mov $0b11, %rdx # prot = PROT_READ 0b1 | PROT_WRITE 0b10
	mov $0x22, %r10 # flags = MAP_PRIVATE 0x2 | MAP_ANONYMOUS 0x20
	mov $-1, %r8 # fd = -1
	mov $0, %r9 # off = 0
	syscall

	# store the results
	mov %rax, _write_region_start
	add %r14, %rax
	mov %rax, _write_region_end
	mov %rax, %rsp # initialize writing into the new region

	jmp *%r15

_uskel_gc_init:
	mov %rsi, %r13
	movq $0x100, _gc_min_alloc # must be higher than 2x the biggest thunk possible
	movq $0x180, _gc_grow_ratio
	movq $0x40, _gc_shrink_ratio
	mov $0, %rsp # fake original rsp for first alloc run
	mov $_uskel_gc_init_cont, %rsi
	jmp _uskel_alloc
	_uskel_gc_init_cont:
	mov _write_region_start, %rax
	mov %rax, _gc_trigger
	jmp *%r13

_uskel_gc:
	# save what we did before ending up here
	mov %rbp, _gc_backup_thunk
	mov %rsi, _gc_backup_cont

	# first we need a new memory area
	mov _write_region_start, %rbx
	mov _write_region_end, %rcx
	mov %rbx, _gc_region_start
	mov %rcx, _gc_region_end
	mov $_uskel_gc_evacuate, %rsi
	jmp _uskel_alloc
	_uskel_gc_evacuate:

	# point the writer to the new memory area
	mov _write_region_end, %rsp
	mov %rsp, %r8 # % r8 is the "last thing that was scavenged"
	
	# start by evacuating the thunk and cont
	mov _gc_backup_thunk, %rbp
	mov $_uskel_gc_evacuate_cont_thunk, %rsi
	jmp _gc_evacuate
	_uskel_gc_evacuate_cont_thunk:
	mov %rbp, _gc_backup_thunk

	mov _gc_backup_cont, %rbp
	mov $_uskel_gc_evacuate_cont_cont, %rsi
	jmp _gc_evacuate
	_uskel_gc_evacuate_cont_cont:
	mov %rbp, _gc_backup_cont

	# scavenge everything
	_uskel_gc_scavenge:
	# start at what we wrote last
	mov %rsp, %rbp # rbp is the iterator (conveniently)
	mov %rsp, %r9 # % r9 stores where we started with this evacuate round

	# if the thing is already scavenged, we didn't write anything, mark done.
	cmp %rbp, %r8
	jbe _uskel_gc_scavenge_end

	_uskel_gc_scavenge1:
	# if all ok, scavenge one thing (moving %rbp) and recheck
	mov (%rbp), %rax
	jmp *-020(%rax) # scavenge position in infotable
	_gc_scavenge_ret:
	cmp %rbp, %r8
	ja _uskel_gc_scavenge1

	# everything above r9 is now scavenged, continue with next round
	mov %r9, %r8 # we started at r9, so that is now "done"
	jmp _uskel_gc_scavenge

	_uskel_gc_scavenge_end:
	# deallocate the old memory region
	mov $11, %rax # munmap
	mov _gc_region_end, %rsi
	mov _gc_region_start, %rdi # addr = gc start
	sub %rdi, %rsi # len = gc end - gc start
	syscall
	
	# recalculate the gc trigger point
	mov %rsp, %rax
	sub _write_region_start, %rax
	mulq _gc_shrink_ratio
	shr $8, %rax
	add _write_region_start, %rax
	mov %rax, _gc_trigger

	# save how much data we actually had at this point
	mov _write_region_end, %rax
	sub %rsp, %rax
	mov %rax, _gc_last_size

	# restore what we were doing
	mov _gc_backup_thunk, %rbp
	mov _gc_backup_cont, %rsi
	enter_rbp # for simplicity just restart the thunk

_gc_evacuate:
	# check if we are really out of the target region
	cmp _write_region_start, %rbp
	jb _gc_evacuate_go
	cmp _write_region_end, %rbp
	jae _gc_evacuate_go
	_gc_evacuate_skip:
	# if not, let's just jump to cont and leave %rbp as result
	jmp *%rsi
	_gc_evacuate_go:
	# if we should evacuate, jump to the evac routine
	mov %rbp, %r10
	mov (%rbp), %rax
	jmp *-030(%rax)
	_gc_evacuate_ret:
	# install the indirection
	movq $IND_code, 000(%r10)
	mov %rbp, 010(%r10)
	jmp *%rsi


.endif #_gc_s_file