# heap a heap is allocated at the start, and the address to the next free space on it is stored in `heap_start`. it needs to be updated/decremented whenever you put anything on the heap ## todo - how should allocation be managed? make free blocks a doubly linked list. (how big is a block?) when allocating, use first free block, move up free pointer when freeing, do ??? # ownership ``` (let ([s "hello"]) <- s should be a linear string (mkpair (lambda () (print s)) <- two references to s? (lambda () (print (reverse s))))) ``` # closures * lambda: actual function containing the code * closure: reference to the lambda containing captives closures are stored on the heap. They contain the address to the lambda along with any "captives", i.e. captured variables. They have the following layout: ``` 0 8 16 24 32 lambda code 1st 2nd 3rd address captive captive captive ... ``` ## note on recursive closures The following example shows a recursive lambda, that results in a closure that captures itself. ``` (let ([f (closure lambda0 (f))]) (f 42)) ``` When this happens, `codegen-let` will insert `(f . 'self-captive)` into the environment when codegen'ing the closure. `codegen-closure` will then pick this up, and use it to insert its own heap address into its inner environment. # lambdas lambdas use the system v amd64 calling convention. * param 0: the start address of the **1st** captive * param 1...n: the remaining, regular args. e.g. ``` (let [(x 42)] (lambda (y) (+ x y))) ``` * param 0: pointer to the value of `x` * param 1: the value of`y` # inter-function and stack values ints, bools and closures are passed around within functions in `%rax`. adts are passed on the stack, at whatever stack index the code generation was called with.