calling convention ------------------ fpx clx pcx argn ... arg2 arg1 fp0-> narg local1 local2 ... localn temp1 temp2 ... sp0-> tempn frame L: sp -= 3 stack[sp] = L stack[sp+1] = cl stack[sp+2] = fp ret: let* narg = stack[fp+1] sp = fp-(narg+1) in pc = stack[sp] cl = stack[sp+1] fp = stack[sp+2] sp += 3 apply C: cl = C pc = C.entry fp = sp; call sequence looks like: frame code for args call : ; return value in %ac why there is both a frame and call insn - simplifies stack fixup on tail calls - we just slide the arguments up stack may have calls in progress -- argument evaluations in frame all stack locations are live except potentially those local and temp slots not being currently used. live mask: which of fp[-1] ... fp[-(l+t)] is "live" l = #locs t = #tmps live really means defined all other locations through sp are live assume tail calls just work (just argument shift) store frame size and live mask at return point to record which stack locations are live need to understand unwind procedure used by gc including in presence of tail calls