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
|
.include "include/data.s"
#TODO apply1 seems obsolete by generic apply
# | fun | arg | -> cont
.thunkcode apply1
needs_alloc $050
thunkto %rsi, $apply1_fini, $3, 030(%rbp), %rbp, %rsi
enter 020(%rbp) # evaluate fun
# fun -> | arg | ret | cont |
.thunkcode apply1_fini
# we now know that fun points to a FUN with at least one arg missing.
# we're certainly going to copy a lot of args.
mov 020(%rsi), %r11 # amount of args applied now
# prepare enough memory for the worst case alloc (make FUN from arg count + 3)
lea 030(,%r11,010), %r12
needs_alloc %r12
# the copying code is shared so let's do that first:
pushq 020(%rbp) #push the new arg
lea 030(%rsi), %rdx # the end (first arg)
lea (%rdx, %r11, 010), %rbx # address behind the last arg
cmp %rdx, %rbx
jbe apply1_fini_cont
apply1_fini_copy:
sub $010, %rbx # iterate down
pushq (%rbx) # push what we have
cmp %rdx, %rbx # check if we are at the end
ja apply1_fini_copy # if not, continue
apply1_fini_cont:
add $1, %r11
pushq %r11 # new number of args of fun/thunk
pushq 010(%rsi) # thunk code pointer
# copying of all args and their thunky header is now done, let's find
# out how we need to finish it.
mov (%rsi), %rdi # infotable for the original fun
mov -010(%rdi), %r12 # amount of args required to make the thunk
cmp %r11, %r12
ja apply1_fini_feed # not enough args, just make a bigger FUN
# if there was enough args, we simply have a thunk that we want to
# continue evaluating, so let's jump to it.
mov 030(%rbp), %rdi # load the original thunk
mov %rsp, 010(%rdi) # set indirect to the new thunk
movq $IND_code, 0(%rdi)
mov 040(%rbp), %rsi # set continuation to the original continuation
enter %rsp # evaluate the new thunk
apply1_fini_feed:
# if there were not enough args, we push the function info and return
pushq (%rsi) # copy the function infoptr
mov 030(%rbp), %rdi # load the original thunk
mov %rsp, 010(%rdi) # set the indirect to the new FUN
movq $IND_code, 0(%rdi)
mov %rsp, %rsi # return the new FUN
enter 040(%rbp) # jump to the continuation
# | fun | arg[1] | arg[2] | ... | arg[args-1] | -> cont
.thunkcode apply
needs_alloc $040
thunkto %rsi, $apply_fini, $2, %rbp, %rsi
enter 020(%rbp)
# fun -> | ret (with args) | cont |
.thunkcode apply_fini
mov (%rsi), %r9 # infotable for the original function
mov 020(%rbp), %r10 # the original thunk
mov 020(%rsi), %r11 # amount of args applied in the closure
mov -010(%r9), %r12 # amount of args required to make a thunk
mov 010(%r10), %r13 # amount of args in the original thunk
sub $1, %r13 # amount of args we want to apply (the 1st one is the FUN)
lea (%r11, %r13), %r14 # total amount arguments we have
lea 050(%r14), %r15 # how much memory this needs in extreme
needs_alloc %r15
# worst-case memory is: we make a thunk (2 headers + some args) and a
# leftover closure (3 headers + rest of args)
# Now that we have enough memory, do we have enough arguments to do anything?
cmp %r12, %r14
ja apply_fini_o
apply_fini_pt:
# Not enough args or exactly enough args.
# Basically make a function or a thunk that is almost the same.
# first move thunk params
mov %r13, %rcx
cmp $0, %rcx
jz apply_fini_pt_thunk_skip
lea 030(%r10, %r13, 010), %rdx
apply_fini_pt_thunk_copy:
sub $010, %rdx
pushq (%rdx)
loop apply_fini_pt_thunk_copy
apply_fini_pt_thunk_skip:
# now move the fun params
mov %r11, %rcx
cmp $0, %rcx
jz apply_fini_pt_fun_skip
lea 030(%rsi, %r11, 010), %rdx
apply_fini_pt_fun_copy:
sub $010, %rdx
pushq (%rdx)
loop apply_fini_pt_fun_copy
apply_fini_pt_fun_skip:
# make a thunk
thunk 010(%rsi), %r14
cmp %r12, %r14 # are we precisely at the right amount of arguments for a thunk?
je apply_fini_pt_thunk # if not, wrap a closure
apply_fini_pt_closure:
thunkto %rsi, %r9
# replace the original thunk with an indirect
mov %rsi, 010(%r10)
movq $IND_code, (%r10)
# return the closure (%rsi) to the original continuation
enter 030(%rbp)
apply_fini_pt_thunk:
# it is a thunk, point to it and start evaluating it
mov %rsp, 010(%r10)
movq $IND_code, (%r10)
# tell the thunk to evaluate into the original continuation
mov 030(%rbp), %rsi
enter %rsp
apply_fini_o: #TODO needs to be tested
# too many args, we need to split off a bit
# first move just the right amount of args off the thunk
mov %r12, %rcx
sub %r11, %rcx
cmp $0, %rcx
jz apply_fini_o_tc_skip
lea 030(%r10, %rcx, 010), %rdx
apply_fini_o_tc_copy:
sub $010, %rdx
pushq (%rdx)
loop apply_fini_o_tc_copy
apply_fini_o_tc_skip:
# move all args from the closure
mov %r11, %rcx
cmp $0, %rcx
jz apply_fini_o_fun_skip
lea 030(%rsi, %r11, 010), %rdx
apply_fini_o_fun_copy:
sub $010, %rdx
pushq (%rdx)
loop apply_fini_o_fun_copy
apply_fini_o_fun_skip:
# make the thunk for the application that can be evaluated later
thunkto %r15, 010(%rsi), %r14
# now make a thunk with the rest of the stuff
mov %r14, %rcx
sub %r12, %rcx
mov %rcx, %r14 # backup leftover count for later
cmp $0, %rcx
jz apply_fini_o_tt_skip
lea 030(%r10, %r13, 010), %rdx
apply_fini_o_tt_copy:
sub $010, %rdx
pushq (%rdx)
loop apply_fini_o_tt_copy
apply_fini_o_tt_skip:
# finish the leftovers thunk
add $1, %r14 # (1 fun to apply to + args)
thunk $apply,%r14,%r15
# replace the original thunk with an indirect
mov %rsp, 010(%r10)
movq $IND_code, (%r10)
# evaluate to the original continuation
mov 030(%rbp), %rsi
enter %rsp
|