From f7faacf80668c7e617ccb30a7ed83165084f289a Mon Sep 17 00:00:00 2001 From: Stephen Adams Date: Sat, 5 Aug 1995 16:25:13 +0000 Subject: [PATCH] Initial revision --- v8/src/compiler/documentation/cmpaux.txt | 490 +++ v8/src/compiler/documentation/cmpint.txt | 1093 ++++++ v8/src/compiler/documentation/facts.txt | 28 + v8/src/compiler/documentation/files.txt | 197 + v8/src/compiler/documentation/notes.txt | 147 + v8/src/compiler/documentation/porting.guide | 3751 +++++++++++++++++++ v8/src/compiler/documentation/safety.txt | 226 ++ v8/src/compiler/documentation/todo.txt | 158 + 8 files changed, 6090 insertions(+) create mode 100644 v8/src/compiler/documentation/cmpaux.txt create mode 100644 v8/src/compiler/documentation/cmpint.txt create mode 100644 v8/src/compiler/documentation/facts.txt create mode 100644 v8/src/compiler/documentation/files.txt create mode 100644 v8/src/compiler/documentation/notes.txt create mode 100644 v8/src/compiler/documentation/porting.guide create mode 100644 v8/src/compiler/documentation/safety.txt create mode 100644 v8/src/compiler/documentation/todo.txt diff --git a/v8/src/compiler/documentation/cmpaux.txt b/v8/src/compiler/documentation/cmpaux.txt new file mode 100644 index 000000000..d56587604 --- /dev/null +++ b/v8/src/compiler/documentation/cmpaux.txt @@ -0,0 +1,490 @@ +-*- Text -*- + +$Header: /Users/cph/tmp/foo/mit-scheme/mit-scheme/v8/src/compiler/documentation/cmpaux.txt,v 1.1 1995/08/05 16:25:13 adams Exp $ + + +Copyright (c) 1991 Massachusetts Institute of Technology + + + Documentation of the assembly language + interface to MIT Scheme compiled code + *DRAFT* + + + +In the following, whenever Scheme is used, unless otherwise specified, +we refer to the MIT Scheme dialect and its CScheme implementation. + +This file describes the entry points that must be provided by +cmpaux-md.h, and the linkage conventions assumed by scheme code. + +cmpint.txt provides some background information required to understand +what follows. + + Calling conventions + +Most C implementations use traditional stack allocation of frames +coupled with a callee-saves register linkage convention. Scheme would +have a hard time adopting a compatible calling convention: + +The Scheme language requires properly tail recursive implementations. +This means that at any given point in a program's execution, if an +object is not accessible from the global (static) state of the +implementation or the current continuation, the object's storage has +been reclaimed or is in the process of being reclaimed. In +particular, recursively written programs need only use progressively +more storage if there is accumulation in the arguments or deeper +levels of the recursion are invoked with more deeply nested +continuations. + +This seemingly abstract requirement of Scheme implementations has some +very mundane consequences. The traditional technique of allocating a +new stack frame for each nested procedure call and deallocating it on +return does not work for Scheme, since allocation should only take +place if the continuation grows, not if the nesting level grows. + +A callee-saves register convention is hard to use for Scheme. The +callee-saves convention assumes that all procedures entered eventually +return, but in the presence of tail recursion, procedures replace each +other in the execution call tree and return only infrequently. The +caller-saves convention is much better suited to Scheme, since +registers can be saved when the continuation grows and restored when +it shrinks. + +Given these difficulties, it is easier to have each language use its +natural convention rather than impose an inappropriate (and therefore +expensive) model on Scheme. + +The unfortunate consequence of this decision is that Scheme and C are +not trivially inter-callable, and thus interface routines must be +provided to go back and forth. + +One additional complication is that the Scheme control stack (that +represents the current continuation) must be examined by the garbage +collector to determined what storage is currently in use. This means +that it must contain only objects or that regions not containing +objects must be clearly marked. Again, it is easier to have separate +stacks for Scheme and C than to merge them. + +The interface routines switch between stacks as well as between +conventions. They must be written in assembly language since they do +not follow C (or Scheme, for that matter) calling conventions. + + Routines required by C: + +The C support for compiled code resides in cmpint.c and is customized +to the implementation by cmpint2.h which must be a copy of the +appropriate cmpint-md.h file. + +The code in cmpint.c provides the communication between compiled code +and the interpreter, primitive procedures written in C, and many +utility procedures that are not in-line coded. + +cmpint.c requires three entry points to be made available from +assembly language: + +* C_to_interface: + This is a C-callable routine (it expects its arguments and return + address following C's passing conventions) used to transfer from the C + universe to the compiled Scheme universe. + + It expects a single argument, namely the address of the instruction to + execute once the conventions have been switched. + + It saves all C callee-saves registers, switches stacks (if there is + an architecture-distinguished stack pointer register), initializes + the Scheme register set (Free register, register array pointer, + utility handles register, MemTop register, pointer mask, value + register, dynamic link register, etc.), and jumps to the address + provided as its argument. + + C_to_interface does not return directly, it tail-recurses into + Scheme. Scheme code will eventually invoke C utilities that will + request a transfer back to the C world. This is accomplished by + using one of the assembly-language provided entry points listed below. + +C utilities are invoked as subroutines from an assembly language +routine (scheme_to_interface) described below. The expectation is +that they will accomplish their task, and execution will continue in +the compiled Scheme code, but this is not always the case, since the +utilities may request a transfer to the interpreter, the error system, +etc. + +This control is accomplished by having C utilities return a C +structure with two fields. The first field must hold as its contents +the address of one of the following two entry points in assembly +language. The second field holds a value that depends on which entry +point is being used. + +* interface_to_C: + This entry point is used by C utilities to abandon the Scheme + compiled code universe, and return from the last call to + C_to_interface. Its argument is the (C long) value that + C_to_interface must return to its caller. It is typically an exit + code that specifies further action by the interpreter. + + interface_to_C undoes the work of C_to_interface, ie. it saves the + Scheme stack and Free memory pointers in the appropriate C variables + (Ext_Stack_Pointer and Free), restores the C linkage registers and + callee-saves registers previously saved, and uses the C return + sequence to return to the caller of C_to_interface. + +* interface_to_scheme: + This entry point is used by C utilities to continue executing in the + Scheme compiled code universe. Its argument is the address of the + (compiled Scheme) instruction to execute once the transfer is + finished. + + Typically C_to_interface and interface_to_scheme share code. + + Routines required by Scheme: + +Conceptually, only one interface routine is required by Scheme code, +namely scheme_to_interface. For convenience, other assembly language +routines are may be provided with slightly different linkage +conventions. The Scheme compiler back end will choose among them +depending on the context of the call. Longer code sequences may have +to be issued if only one of the entry points is provided. The other +entry points are typically a fixed distance from scheme_to_interface, +so that compiled code can invoke them by adding the fixed offset. + +* scheme_to_interface: + This entry point is used by Scheme code to invoke a C utility. + It expects up to five arguments in fixed registers. The first + argument (required), is the number identifying the C utility routine + to invoke. This number is the index of the location + containing the address of the C procedure in the array + utility_result, declared in cmpint.c . + + The other four registers contain the arguments to be passed to the + utility procedure. Note that all C utilities declare 4 arguments + even if fewer are necessary or relevant. The reason for this is + that the assembly language routines must know how many arguments to + pass along, and it is easier to pass all of them. Of course, + compiled code need not initialize the registers for those arguments + that will never be examined. + + In order to make this linkage as fast as possible, it is + advantageous to choose the argument registers from the C argument + registers if the C calling convention passes arguments in registers. + In this way there is no need to move them around. + + scheme_to_interface switches stacks, moves the arguments to the + correct locations (for C), updates the C variables Free and + Ext_Stack_Pointer, and invokes (in the C fashion) the C utility + procedure indexed by the required argument to scheme_to_interface. + + On return from the call to scheme_to_interface, a C structure + described above is expected, and the first component is invoked + (jumped into) leaving the structure or the second component in a + pre-established place so that interface_to_C and interface_to_scheme + can find it. + +* scheme_to_interface_ble/jsr: + Many utility procedures expect a return address as one of their + arguments. The return address can be easily obtained by using the + machine's subroutine call instruction, rather than a jump + instruction, but the return address may not be left in the desired + argument register. scheme_to_interface_ble/jsr can be provided to + take care of this case. In order to facilitate its use, all utility + procedures that expect a return address receive it as the first + argument. Thus scheme_to_interface_ble/jsr is invoked with the + subroutine-call instruction, transfers (and bumps past the format + word) the return address from the place where the instruction leaves + it to the first argument register, and falls through to + scheme_to_interface. + +* trampoline_to_interface: + + Many of the calls to utilities occur in code issued by the linker, + rather than the compiler. For example, compiled code assumes that + all free operator references will resolve to compiled procedures, + and the linker constructs dummy compiled procedures (trampolines) to + allow the code to work when they resolve to unknown or interpreted + procedures. Corrective action must be taken on invocation, and this + is accomplished by invoking the appropriate utility. Trampolines + contain instructions to invoke the utility and some + utility-dependent data. The instruction sequence in trampolines may + be shortened by having a special-purpose entry point, also invoked + with a subroutine-call instruction. All utilities expecting + trampoline data expect as their first argument the address of the + first location containing the data. Thus, again, the return address + left behind by the subroutine-call instruction must be passed in the + first argument register. + +scheme_to_interface_ble/jsr and trampoline_to_interface are virtually +identical. The difference is that the return address is interpreted +differently. trampoline_to_interface interprets it as the address of +some storage, scheme_to_interface_ble/jsr interprets it as a machine +return address that must be bumped to a Scheme return address (all +Scheme entry points are preceded by format words for the garbage +collector). + +More entry points can be provided for individual ports. Some ports +have entry points for common operations that take many instructions +like integer multiplication, allocation and initialization of a +closure object, or calls to unknown Scheme procedures with fixed +numbers of arguments. None of these are necessary, but may make the +task of porting the compiler easier. + +Typically these additional entry points are also a fixed distance away +from scheme_to_interface in order to reduce the number of reserved +registers required. + + Examples: + +1 (PDP-11-like CISC): + +Machine M1 is a general-addressing-mode architecture and has 7 +general-purpose registers (R0 - R6), and a hardware-distinguished +stack pointer (SP). The stack is pushed by predecrementing the stack +pointer. The JSR (jump to subroutine) instruction transfers control +to the target and pushes a return address on the stack. The RTS +(return from subroutine) instruction pops a return address from the +top of the stack and jumps to it. + +The C calling convention is as follows: + +- arguments are passed on the stack and are popped on return by the +caller. +- the return address is on top of the stack on entry. +- register r6 is used as a frame pointer, saved by callees. +- registers r0 - r2 are caller saves, r3 - r5 are callee saves. +- scalar values are returned in r0. +- structures are returned by returning the address of a static area on r0. + +The Scheme register convention is as follows: + +- register r6 is used to hold the register block. +- register r5 is used to hold the free pointer. +- register r4 is used to hold the dynamic link, when necessary. +- registers r1 - r3 are the caller saves registers for the compiler. +- register r0 is used as the value register by compiled code. +- all other implementation registers reside in the register array. + In addition, scheme_to_interface, trampoline_to_interface, etc., are + reached from the register array as well (there is an absolute jump + instruction in the register array for each of them). + +The utility calling convention is as follows: + +- the utility index is in r0. +- the utility arguments appear in r1 - r4. + +The various entry points would then be (they can be bummed): + +_C_to_interface: + push r6 ; save old frame pointer + mov sp,r6 ; set up new frame pointer + mov 8(r6),r1 ; argument to C_to_interface + push r3 ; save callee-saves registers + push r4 + push r5 + push r6 ; and new frame pointer + +_interface_to_scheme: + mov sp,_saved_C_sp ; save the C stack pointer. + mov _Ext_Stack_Pointer,sp ; set up the Scheme stack pointer + mova _Registers,r6 ; set up the register array pointer + mov _Free,r5 ; set up the free register + mov regblock_val(r6),r0 ; set up the value register + and &,r0,r4 ; set up the dynamic link register + jmp 0(r1) ; go to compiled Scheme code + +scheme_to_interface_jsr: + pop r1 ; return address is first arg. + add &4,r1,r1 ; bump past format word + jmp scheme_to_interface + +trampoline_to_interface: + pop r1 ; return address is first arg. + +scheme_to_interface: + mov sp,_Ext_Stack_Pointer ; update the C variables + mov r5,_Free + mov _saved_C_sp,sp + mov 0(sp),r6 ; restore C frame pointer + push r4 ; push arguments to utility + push r3 + push r2 + push r1 + mova _utility_table,r1 + mul &4,r0,r0 ; scale index to byte offset + add r0,r1,r1 + mov 0(r1),r1 + jsr 0(r1) ; invoke the utility + + add &16,sp,sp ; pop arguments to utility + mov 4(r0),r1 ; extract argument to return entry point + mov 0(r0),r0 ; extract return entry point + jmp 0(r0) ; invoke it + +_interface_to_C: + mov r1,r0 ; C return value + pop r6 ; restore frame pointer + pop r5 ; and callee-saves registers + pop r4 + pop r3 + pop r6 ; restore caller's frame pointer + rts ; return to caller of C_to_interface + +Note that somewhere in the register array there would be a section +with the following code: + +offsi jmp scheme_to_interface +offsj jmp scheme_to_interface_jsr +offti jmp trampoline_to_interface + < perhaps more > + +So that the compiler could issue the following code to invoke utilities: + + + mov &,r0 + jmp offsi(r6) + +or + + + mov &,r0 + jsr offsj(r6) + + +