Skip to content

Commit 83df854

Browse files
committed
Correct implementation of Knock:
* Fix stack alignment, we couldn't tickle the bug, but we know it was there * Implement tail-call for `(call (fun f) x y z)` * Add a few tests
1 parent 8bd0a62 commit 83df854

3 files changed

Lines changed: 74 additions & 13 deletions

File tree

langs/knock/compile.rkt

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
[(App f es) (if (<= (length es) s)
6464
(compile-tail-call f es c)
6565
(compile-app f es c))]
66+
[(FCall e1 es) (if (<= (length es) s)
67+
(compile-tail-fun-call e1 es c)
68+
(compile-fun-call e1 es c))]
6669
[(Begin e1 e2) (compile-tail-begin e1 e2 c s)]
6770
[_ (compile-e e c)])))
6871

@@ -104,17 +107,6 @@
104107
; Bump the heap pointer
105108
(Add rbx 8)))
106109

107-
(define (compile-fun-call e es c)
108-
(let ((d (length es)))
109-
(seq (compile-e e c)
110-
(assert-proc rax)
111-
(Push rax)
112-
(compile-es es (cons #f c))
113-
(Mov rax (Offset rsp (* 8 d)))
114-
(Xor rax type-proc)
115-
(Call (Offset rax 0))
116-
(Add rsp (* 8 (add1 d))))))
117-
118110
;; Op0 CEnv -> Asm
119111
(define (compile-prim0 p c)
120112
(match p
@@ -248,6 +240,50 @@
248240
(Add rsp (* 8 (+ cnt (in-frame c))))
249241
(Jmp (symbol->label f)))))
250242

243+
;; Similar to `compile-app` we have to be concerned about 16-byte alignment
244+
;; of `rsp`. However, the wrinkle is that we also have the function pointer
245+
;; on the stack, so we have to do the calculation with an `extended` env: `env`
246+
(define (compile-fun-call e es c)
247+
(let ((d (length es))
248+
(env (cons #f c)))
249+
; We have to computer the function pointer either way.
250+
(seq (compile-e e c)
251+
(assert-proc rax)
252+
(Push rax)
253+
254+
; Then we worry about alignment
255+
(if (even? (+ d (length env)))
256+
257+
; We will be 16-byte aligned
258+
(seq (compile-es es env)
259+
(Mov rax (Offset rsp (* 8 d)))
260+
(Xor rax type-proc)
261+
(Call (Offset rax 0))
262+
(Add rsp (* 8 (add1 d))))
263+
264+
; We won't be 16-byte aligned, and need to adjust `rsp`
265+
(seq (Sub rsp 8)
266+
(compile-es es env)
267+
(Mov rax (Offset rsp (* 8 (add1 d))))
268+
(Xor rax type-proc)
269+
(Call (Offset rax 0))
270+
; pop arguments, padding, and function pointer
271+
(Add rsp (* 8 (+ 2 d))))))))
272+
273+
;; Variable (Listof Expr) CEnv -> Asm
274+
;; Compile a call in tail position
275+
(define (compile-tail-fun-call f es c)
276+
(let ((cnt (length es)))
277+
(seq (compile-e f c)
278+
(assert-proc rax)
279+
(Push rax)
280+
(compile-es es (cons #f c))
281+
(move-args cnt (+ cnt (add1 (in-frame c))))
282+
(Mov rax (Offset rsp (* 8 cnt)))
283+
(Xor rax type-proc)
284+
(Add rsp (* 8 (+ cnt (add1 (in-frame c)))))
285+
(Jmp (Offset rax 0)))))
286+
251287
;; Integer Integer -> Asm
252288
;; Move i arguments upward on stack by offset off
253289
(define (move-args i cnt)

langs/knock/interp.rkt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
;; | Integer
1111
;; | Boolean
1212
;; | Character
13+
;; | (Fun f)
1314
;; | Eof
1415
;; | Void
1516
;; | '()
@@ -70,8 +71,22 @@
7071
; arity check
7172
(if (= (length vs) (length xs))
7273
(interp-env body (zip xs vs) ds)
73-
'err)])]
74-
[_ 'err])]))
74+
'err)])])]
75+
[(Fun f)
76+
(match (defns-lookup ds f)
77+
[(Defn f xs body)
78+
(lambda (es r)
79+
(match (interp-env* es r ds)
80+
[(list vs ...)
81+
(if (= (length vs) (length xs))
82+
(interp-env body (zip xs vs) ds)
83+
'err)]))]
84+
[_ 'err])]
85+
[(FCall f es)
86+
(match (interp-env f r ds)
87+
[(? procedure? f) (f es r)]
88+
[_ 'err])]
89+
[_ 'err]))
7590

7691
;; (Listof Expr) REnv Defns -> (Listof Value) | 'err
7792
(define (interp-env* es r ds)

langs/knock/test/test-runner.rkt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@
104104
(+ x (tri (sub1 x)))))
105105
(tri 9)))
106106
45)
107+
;; Knock tests
108+
(check-equal? (run
109+
'(begin (define (f x) x)
110+
(call (fun f) 42)))
111+
42)
112+
(check-equal? (run
113+
'(begin (define (f x) x)
114+
(define (g x) x)
115+
(call (car (cons (fun f) (cons (fun g) '()))) 42)))
116+
42)
107117
#|
108118
(check-equal? (run
109119
'(begin (define (even? x)

0 commit comments

Comments
 (0)