+ (emit "movq ~a(%rbp), %rax" offset)))
+
+(define cur-lambda 0)
+(define (fresh-lambda)
+ (set! cur-lambda (+ 1 cur-lambda))
+ (format "_lambda~a" (- cur-lambda 1)))
+
+; a closure on the heap looks like:
+; 0-x x+0 x+4 x+12 x+20
+; label #vars var1.... var2.... var3....
+(define (codegen-closure label captured si env)
+ (let* ((heap-offsets (range 4 (length captured))) ; 4, 12, 20, etc.
+ (inner-si (- si (* (length captured) wordsize))))
+ (emit "movl $~a, (heap_start)")
+ (emit "add $4, (heap_start)")
+ (for-each (lambda (var-name new-offset)
+ (emit "movq ~a(%rbp), ~a(heap_start)" ; todo: do we need to copy this?
+ (cdr (assoc var-name env))
+ new-offset)
+ (emit "add $8, (heap_start)")
+ captured
+ stack-offsets)
+)
+ ; for now we can only call closures
+(define (codegen-call closure args si env)
+; (codegen-expr closure si env)
+ (when (not (eq? (ast-type closure) 'closure))
+ (error #f (format "~a is not a closure" closure)))
+ (let* ((captured (caddr closure))
+ (label (cadr closure))
+ (argument-start (length captured)))
+
+ ; first move the captured variables into param registers
+ (for-each
+ (lambda (e i)
+ (emit "movq ~a(%rbp), ~a"
+ (cdr (assoc e env)) ; offset of the var
+ (param-register i)))
+ captured (range 0 (length captured)))
+
+ ; then codegen the arguments and move them into the next param registers
+ (for-each
+ (lambda (e i)
+ (begin
+ (codegen-expr e si env)
+ ; move result to correct param register
+ (emit "movq %rax, ~a" (param-register i))))
+ args (range argument-start (length args)))
+
+ (emit "addq $~a, %rsp" si) ; adjust the stack pointer to account all the stuff we put in the env
+ (emit "callq ~a" label)
+ (emit "subq $~a, %rsp" si)))
+
+(define (codegen-lambda l)
+ (let* ((label (car l))
+ (args (cadr l))
+ (captured (caddr l))
+ (body (cadddr l))
+; params = what actually gets passed
+ (params (append captured args))
+
+ (param-registers (map param-register
+ (range 0 (length params))))
+ (stack-offsets (map (lambda (i)
+ (* (- wordsize) i))
+ (range 1 (length params))))
+
+ (copy-insts (map (lambda (r o)
+ (format "movq ~a, ~a(%rbp)" r o))
+ param-registers stack-offsets))
+
+ (env (map cons params stack-offsets)))
+ (emit "~a:" label)
+ (display "## lambda body: ")
+ (display body)
+ (newline)
+ (display "## environment: ")
+ (display env)
+ (newline)
+
+ (emit "push %rbp") ; preserve caller's base pointer
+ (emit "movq %rsp, %rbp") ; set up our own base pointer
+
+ (for-each emit copy-insts)
+ (codegen-expr body (* (- wordsize) (+ 1 (length params))) env)
+
+ (emit "pop %rbp") ; restore caller's base pointer
+ (emit "ret")))
+
+(define cur-label 0)
+(define (fresh-label)
+ (set! cur-label (+ 1 cur-label))
+ (format "label~a" (- cur-label 1)))
+
+(define (codegen-if cond then else si env)
+ (codegen-expr cond si env)
+ (emit "cmpq $0, %rax")
+ (let ((exit-label (fresh-label))
+ (else-label (fresh-label)))
+ (emit "je ~a" else-label)
+ (codegen-expr then si env)
+ (emit "jmp ~a" exit-label)
+ (emit "~a:" else-label)
+ (codegen-expr else si env)
+ (emit "~a:" exit-label)))