Input:
((lambda (g x) (g x)) (lambda (x) (prim-apply + 1 x)) 2)
Problem: generated code contains two functions.
We want to jump to (lambda (g x) (g x))
(label_0
) first.
Instead, the code jumps to the (lambda (x) (prim-apply + 1 x))
(label_1
) first.
(gdb) p $label_1
$2 = {<text variable, no debug info>} 0x565e6a30 <label_1>
(gdb) p $label_0
$3 = {<text variable, no debug info>} 0x565e6a42 <label_0>
We want to jump to label_0
first.
Dissecting the evaluation of arguments. First we evaluate the closure at
label_1
:
movl 16(%esp), %esi
movl $label_1, %eax
movl %eax, 0(%esi)
movl %esi, %eax
orl $6, %eax
addl $4, %esi
movl %eax, -12(%esp)
After movl $label_1, %eax
in %eax
: 0x565e6a30
Then we move %eax
to the location pointed to by %esi
, our heap pointer: movl %eax, 0(%esi)
Let's see if it's there. Here is what %esi
contains:
(gdb) p/x $esi
$5 = 0xf7187010
And here is what is at 0xf7187010
:
(gdb) x $esi
0xf7187010: 0x565e6a30
Our label.
Then %esi
gets copied to %eax
, because this pointer is the result of the
evaluation.
movl %esi, %eax
Now we need to tag it.
Before, here are hex, binary, decimal representations:
0xf7187010 11110111000110000111000000010000 4145573904
0x565e6a30 01010110010111100110101000110000 1449028144
What we want to tag is 0xf7187010
Now, after executing orl $6, %eax
:
(gdb) x $eax
0xf7187016: 0x00000000
That is
0xf7187016 11110111000110000111000000010110 4145573910
Our tag is 6
, which is 110
, so the tag worked.
Then we evaluate the next argument, the 2
:
movl $8, %eax
movl %eax, -16(%esp)
Then we evaluate the closure we want to call:
movl $label_0, %eax
movl %eax, 0(%esi)
movl %esi, %eax
orl $6, %eax
addl $4, %esi
After movl $label_0, %eax
in %eax
: 0x565e6a42
Then we also move %eax
to the location pointed to by %esi
, our heap pointer: movl %eax, 0(%esi)
Let's see if it's there. Here is what %esi
contains:
(gdb) p/x $esi
$7 = 0xf7187014
And here is what is at 0xf7187014
:
(gdb) x $esi
0xf7187014: 0x565e6a42
That is $label_0
. Looks good. Move it to %eax
:
(gdb) x $eax
0xf7187014: 0x565e6a42
0xf7187014 11110111000110000111000000010100 4145573908
0x565e6a42 01010110010111100110101001000010 1449028162
And now we tag it in %eax
:
0xf7187016 11110111000110000111000000010110 4145573910
0x565e 00000000000000000101011001011110 22110
Uh oh. The pointer has only been incremented by 2
.
That means, when we then untag the pointer and do a subl $6, %eax
, we decrease by 6
, which doesn't get us to the correct original pointer.
Realization: the problem is that after we tag the first closure pointer, we only
advance %esi
by a single wordsize, 4
. That is not enough to align the
pointer at 8 bytes, which would allow us to use the lowest 3 bits for tagging.
What we need to do: instead of advancing %esi
by a single word. We need to align it at double-word boundaries.
Yep, that's it. If we change the existing code from
(emit "orl $~a, %eax" object-tag-closure)
(emit "addl $~a, %esi" wordsize)
to this:
(emit "orl $~a, %eax" object-tag-closure)
;; Add 7+4 to pointer in %esi because
;; * 7 to align to multiple of 8
;; * 4 because that's the size of the label we stored and want to skip.
;; then bitwise-AND it with -8
(emit "addl $11, %esi")
(emit "andl $-8, %esi")
Then every test passes!
Thanks for tuning in.