Notes on porting CMUCL to the 386 and 486. Dedicated registers: CSP [ESP], CFP [EBP], MUTATOR, NIL, CODE, NSP, NFP Instead of having NIL in a register use immediates? Have the mutator struct sitting some static place in memory? If we are doing our own task switching, then when can also swap it in and out. Or even diddle segment/page mappings to have different mutator structures visable. Another possibility for MUTATOR is to have it at the top of the stack and compute it from CSP whenever needed. Unify the C and Lisp stacks somehow? Need to teach GC about what to scavenge and what to ignore. Currently, only the lisp stack is ever dynamically grown. Therefore, each stack frame could consist of three sections: stationary lisp noise, non-descriptor noise, dynamic lisp noise. We can't put all the lisp noise in one area because we have to be able to pass parameters someplace. But that means growing the stack would require flushing the memory, or we might get confused by stale data. Polling interrupts? One check per basic block? Would simplify the necessary GC invarents. We could use the interrupt check points as potential GC points and have the compiler emit liveness maps like Moss' M3 stuff. The various stacks could be trivially merged then. Using a large-object-space for code objects means they don't move. Therefore, instead of having $CODE, we can just use immediate absolute addresses to load constants. Would require 6 bytes either way because there is no REG+d16 addressing mode, just REG+d8 and REG+d32. Additionally, return pc's don't need to be fixed up. Values needed for call: LEXENV/FDEFN [EAX], NARGS [ECX], OCFP [on stack] ARG0...ARGn [EDX, EDI, ESI, rest on stack] Values needed for return: VALS [EBX], NARGS [ECX], ARG0...ARGn [EDX, EDI, ESI, rest on stack] Stack during call transfer: ESP-> RetAddr ARGn ... ARG3 CFP-> OCFP After prologue: ESP-> Locals... ARGn ... ARG3 RetAddr CFP-> OCFP Function call: - Allocate stack frame in ARGS - Save FP - Move args into place - Load FDEFN, NARGS - CALL FDEFN[fdefn-raw-function-slot*4 - other-pointer-type] Closure tramp: - Load LEXENV, FDEFN[function-slot*4 - other-pointer-type] - JMP LEXENV[closure-raw-function-slot*4 - function-pointer-type] Unnamed call: - Allocate stack frame - Move args into place - Load LEXENV, NARGS - CALL LEXENV[closure-raw-function-slot*4 - function-pointer-type] Function prologue: - POP CFP[4] ; move the return-address into place. - LEA CSP, [CFP+stack-frame-size] Return single value: - MOV OcfpTemp, CFP[0] - MOV RetAddrTemp, CFP[4] - ADD RetAddrTemp, 2 ; takes 2 cycles and 6 bytes, but two INC's take 4 ; cycles and 2 bytes. - MOV CSP, CFP - MOV CFP, OcfpTemp - JMP RetAddrTemp Return multiple values: - MOV VALS, CFP - MOV CFP, CFP[0] - JMP VALS[4] Receive single value: - MOV CSP, VALS - Use value in DX Receive 2 or 3 values: - JMP short l1 - MOV EDI, NIL - MOV ESI, EDI - MOV VALS, CSP - l1: - MOV CSP, VALS Receive more than 3 values: - JMP short l1 - MOV EDI, NIL - MOV ESI, EDI - MOV VALS, CSP - JMP default-val-3 - l1: - CMP VALS, (fixnum 3) - JBE default-val-3 - MOV Temp, VALS[3*4] - MOV Value3, Temp - CMP VALS, (fixnum 4) - JBE default-val-4 - MOV Temp, VALS[4*4] - MOV Value4, Temp - ... - done: ... - default-val-3: - MOV Value3, NIL - default-val-4: - MOV Value4, NIL - ... - JMP done ;;;; Polling interrupt support. Use the ``bound'' instruction to test for stack overflow. When we take an interrupt, set the stack limit to zero so the next stack check is guarenteed to flame out. In XEPs: - Initialize frame. - Note this location. - BOUND ESP, stack_bounds In {,multiple-,known-}call-local - Note this location - BOUND ESP, stack_bounds - CALL - Note this location In full call: - Note this location - CALL - Note these locations [mv and single] At backwards branches: - Note this location - BOUND ESP, stack_bounds - JMP