home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / mitsch75.zip / scheme-7_5_17-src.zip / scheme-7.5.17 / src / compiler / documentation / cmpaux.txt next >
Text File  |  2000-03-20  |  20KB  |  491 lines

  1. -*- Text -*-
  2.  
  3. $Id: cmpaux.txt,v 1.4 2000/03/21 04:29:53 cph Exp $
  4.  
  5.  
  6. Copyright (c) 1991 Massachusetts Institute of Technology
  7.  
  8.  
  9.         Documentation of the assembly language
  10.         interface to MIT Scheme compiled code
  11.             *DRAFT*
  12.  
  13.  
  14.  
  15. In the following, whenever Scheme is used, unless otherwise specified,
  16. we refer to the MIT Scheme dialect and its CScheme implementation.
  17.  
  18. This file describes the entry points that must be provided by
  19. cmpaux-md.h, and the linkage conventions assumed by scheme code.
  20.  
  21. cmpint.txt provides some background information required to understand
  22. what follows.
  23.  
  24.     Calling conventions
  25.  
  26. Most C implementations use traditional stack allocation of frames
  27. coupled with a callee-saves register linkage convention.  Scheme would
  28. have a hard time adopting a compatible calling convention:
  29.  
  30. The Scheme language requires properly tail recursive implementations.
  31. This means that at any given point in a program's execution, if an
  32. object is not accessible from the global (static) state of the
  33. implementation or the current continuation, the object's storage has
  34. been reclaimed or is in the process of being reclaimed.  In
  35. particular, recursively written programs need only use progressively
  36. more storage if there is accumulation in the arguments or deeper
  37. levels of the recursion are invoked with more deeply nested
  38. continuations.
  39.  
  40. This seemingly abstract requirement of Scheme implementations has some
  41. very mundane consequences.  The traditional technique of allocating a
  42. new stack frame for each nested procedure call and deallocating it on
  43. return does not work for Scheme, since allocation should only take
  44. place if the continuation grows, not if the nesting level grows.
  45.  
  46. A callee-saves register convention is hard to use for Scheme.  The
  47. callee-saves convention assumes that all procedures entered eventually
  48. return, but in the presence of tail recursion, procedures replace each
  49. other in the execution call tree and return only infrequently.  The
  50. caller-saves convention is much better suited to Scheme, since
  51. registers can be saved when the continuation grows and restored when
  52. it shrinks.
  53.  
  54. Given these difficulties, it is easier to have each language use its
  55. natural convention rather than impose an inappropriate (and therefore
  56. expensive) model on Scheme.
  57.  
  58. The unfortunate consequence of this decision is that Scheme and C are
  59. not trivially inter-callable, and thus interface routines must be
  60. provided to go back and forth.
  61.  
  62. One additional complication is that the Scheme control stack (that
  63. represents the current continuation) must be examined by the garbage
  64. collector to determined what storage is currently in use.  This means
  65. that it must contain only objects or that regions not containing
  66. objects must be clearly marked.  Again, it is easier to have separate
  67. stacks for Scheme and C than to merge them.
  68.  
  69. The interface routines switch between stacks as well as between
  70. conventions.  They must be written in assembly language since they do
  71. not follow C (or Scheme, for that matter) calling conventions.
  72.  
  73.     Routines required by C:
  74.     
  75. The C support for compiled code resides in cmpint.c and is customized
  76. to the implementation by cmpint2.h which must be a copy of the
  77. appropriate cmpint-md.h file.
  78.  
  79. The code in cmpint.c provides the communication between compiled code
  80. and the interpreter, primitive procedures written in C, and many
  81. utility procedures that are not in-line coded.
  82.  
  83. cmpint.c requires three entry points to be made available from
  84. assembly language:
  85.  
  86. * C_to_interface:
  87.   This is a C-callable routine (it expects its arguments and return
  88.   address following C's passing conventions) used to transfer from the C
  89.   universe to the compiled Scheme universe.
  90.  
  91.   It expects a single argument, namely the address of the instruction to
  92.   execute once the conventions have been switched.
  93.  
  94.   It saves all C callee-saves registers, switches stacks (if there is
  95.   an architecture-distinguished stack pointer register), initializes
  96.   the Scheme register set (Free register, register array pointer,
  97.   utility handles register, MemTop register, pointer mask, value
  98.   register, dynamic link register, etc.), and jumps to the address
  99.   provided as its argument.
  100.  
  101.   C_to_interface does not return directly, it tail-recurses into
  102.   Scheme.  Scheme code will eventually invoke C utilities that will
  103.   request a transfer back to the C world.  This is accomplished by
  104.   using one of the assembly-language provided entry points listed below.
  105.  
  106. C utilities are invoked as subroutines from an assembly language
  107. routine (scheme_to_interface) described below.  The expectation is
  108. that they will accomplish their task, and execution will continue in
  109. the compiled Scheme code, but this is not always the case, since the
  110. utilities may request a transfer to the interpreter, the error system,
  111. etc. 
  112.  
  113. This control is accomplished by having C utilities return a C
  114. structure with two fields.  The first field must hold as its contents
  115. the address of one of the following two entry points in assembly
  116. language.  The second field holds a value that depends on which entry
  117. point is being used.
  118.  
  119. * interface_to_C:
  120.   This entry point is used by C utilities to abandon the Scheme
  121.   compiled code universe, and return from the last call to
  122.   C_to_interface.  Its argument is the (C long) value that
  123.   C_to_interface must return to its caller.  It is typically an exit
  124.   code that specifies further action by the interpreter.
  125.  
  126.   interface_to_C undoes the work of C_to_interface, ie. it saves the
  127.   Scheme stack and Free memory pointers in the appropriate C variables
  128.   (Ext_Stack_Pointer and Free), restores the C linkage registers and
  129.   callee-saves registers previously saved, and uses the C return
  130.   sequence to return to the caller of C_to_interface.
  131.  
  132. * interface_to_scheme:
  133.   This entry point is used by C utilities to continue executing in the
  134.   Scheme compiled code universe.  Its argument is the address of the
  135.   (compiled Scheme) instruction to execute once the transfer is
  136.   finished.
  137.   
  138.   Typically C_to_interface and interface_to_scheme share code.
  139.  
  140.     Routines required by Scheme:
  141.  
  142. Conceptually, only one interface routine is required by Scheme code,
  143. namely scheme_to_interface.  For convenience, other assembly language
  144. routines are may be provided with slightly different linkage
  145. conventions.  The Scheme compiler back end will choose among them
  146. depending on the context of the call.  Longer code sequences may have
  147. to be issued if only one of the entry points is provided.  The other
  148. entry points are typically a fixed distance from scheme_to_interface,
  149. so that compiled code can invoke them by adding the fixed offset.
  150.  
  151. * scheme_to_interface:
  152.   This entry point is used by Scheme code to invoke a C utility.
  153.   It expects up to five arguments in fixed registers.  The first
  154.   argument (required), is the number identifying the C utility routine
  155.   to invoke.  This number is the index of the location
  156.   containing the address of the C procedure in the array
  157.   utility_result, declared in cmpint.c .
  158.  
  159.   The other four registers contain the arguments to be passed to the
  160.   utility procedure.  Note that all C utilities declare 4 arguments
  161.   even if fewer are necessary or relevant.  The reason for this is
  162.   that the assembly language routines must know how many arguments to
  163.   pass along, and it is easier to pass all of them.  Of course,
  164.   compiled code need not initialize the registers for those arguments
  165.   that will never be examined.
  166.  
  167.   In order to make this linkage as fast as possible, it is
  168.   advantageous to choose the argument registers from the C argument
  169.   registers if the C calling convention passes arguments in registers.
  170.   In this way there is no need to move them around.
  171.     
  172.   scheme_to_interface switches stacks, moves the arguments to the
  173.   correct locations (for C), updates the C variables Free and
  174.   Ext_Stack_Pointer, and invokes (in the C fashion) the C utility
  175.   procedure indexed by the required argument to scheme_to_interface.
  176.     
  177.   On return from the call to scheme_to_interface, a C structure
  178.   described above is expected, and the first component is invoked
  179.   (jumped into) leaving the structure or the second component in a
  180.   pre-established place so that interface_to_C and interface_to_scheme
  181.   can find it.
  182.  
  183. * scheme_to_interface_ble/jsr:
  184.   Many utility procedures expect a return address as one of their
  185.   arguments.  The return address can be easily obtained by using the
  186.   machine's subroutine call instruction, rather than a jump
  187.   instruction, but the return address may not be left in the desired
  188.   argument register.  scheme_to_interface_ble/jsr can be provided to
  189.   take care of this case.  In order to facilitate its use, all utility
  190.   procedures that expect a return address receive it as the first
  191.   argument.  Thus scheme_to_interface_ble/jsr is invoked with the
  192.   subroutine-call instruction, transfers (and bumps past the format
  193.   word) the return address from the place where the instruction leaves
  194.   it to the first argument register, and falls through to
  195.   scheme_to_interface.
  196.  
  197. * trampoline_to_interface:
  198.  
  199.   Many of the calls to utilities occur in code issued by the linker,
  200.   rather than the compiler.  For example, compiled code assumes that
  201.   all free operator references will resolve to compiled procedures,
  202.   and the linker constructs dummy compiled procedures (trampolines) to
  203.   allow the code to work when they resolve to unknown or interpreted
  204.   procedures.  Corrective action must be taken on invocation, and this
  205.   is accomplished by invoking the appropriate utility.  Trampolines
  206.   contain instructions to invoke the utility and some
  207.   utility-dependent data.  The instruction sequence in trampolines may
  208.   be shortened by having a special-purpose entry point, also invoked
  209.   with a subroutine-call instruction.  All utilities expecting
  210.   trampoline data expect as their first argument the address of the
  211.   first location containing the data.  Thus, again, the return address
  212.   left behind by the subroutine-call instruction must be passed in the
  213.   first argument register.
  214.  
  215. scheme_to_interface_ble/jsr and trampoline_to_interface are virtually
  216. identical.  The difference is that the return address is interpreted
  217. differently.  trampoline_to_interface interprets it as the address of
  218. some storage, scheme_to_interface_ble/jsr interprets it as a machine
  219. return address that must be bumped to a Scheme return address (all
  220. Scheme entry points are preceded by format words for the garbage
  221. collector).
  222.  
  223. More entry points can be provided for individual ports.  Some ports
  224. have entry points for common operations that take many instructions
  225. like integer multiplication, allocation and initialization of a
  226. closure object, or calls to unknown Scheme procedures with fixed
  227. numbers of arguments.  None of these are necessary, but may make the
  228. task of porting the compiler easier.  
  229.  
  230. Typically these additional entry points are also a fixed distance away
  231. from scheme_to_interface in order to reduce the number of reserved
  232. registers required.
  233.   
  234.     Examples:
  235.  
  236. 1 (PDP-11-like CISC):
  237.  
  238. Machine M1 is a general-addressing-mode architecture and has 7
  239. general-purpose registers (R0 - R6), and a hardware-distinguished
  240. stack pointer (SP).  The stack is pushed by predecrementing the stack
  241. pointer.  The JSR (jump to subroutine) instruction transfers control
  242. to the target and pushes a return address on the stack.  The RTS
  243. (return from subroutine) instruction pops a return address from the
  244. top of the stack and jumps to it.
  245.  
  246. The C calling convention is as follows:
  247.  
  248. - arguments are passed on the stack and are popped on return by the
  249. caller.
  250. - the return address is on top of the stack on entry.
  251. - register r6 is used as a frame pointer, saved by callees.
  252. - registers r0 - r2 are caller saves, r3 - r5 are callee saves.
  253. - scalar values are returned in r0.
  254. - structures are returned by returning the address of a static area on r0.
  255.  
  256. The Scheme register convention is as follows:
  257.  
  258. - register r6 is used to hold the register block.
  259. - register r5 is used to hold the free pointer.
  260. - register r4 is used to hold the dynamic link, when necessary.
  261. - registers r1 - r3 are the caller saves registers for the compiler.
  262. - register r0 is used as the value register by compiled code.
  263. - all other implementation registers reside in the register array.  
  264.   In addition, scheme_to_interface, trampoline_to_interface, etc., are
  265.   reached from the register array as well (there is an absolute jump 
  266.   instruction in the register array for each of them).
  267.  
  268. The utility calling convention is as follows:
  269.  
  270. - the utility index is in r0.
  271. - the utility arguments appear in r1 - r4.
  272.  
  273. The various entry points would then be (they can be bummed):
  274.  
  275. _C_to_interface:
  276.     push    r6            ; save old frame pointer
  277.     mov    sp,r6            ; set up new frame pointer
  278.     mov    8(r6),r1        ; argument to C_to_interface
  279.     push    r3            ; save callee-saves registers
  280.     push    r4
  281.     push    r5
  282.     push    r6            ; and new frame pointer
  283.  
  284. _interface_to_scheme:
  285.     mov    sp,_saved_C_sp        ; save the C stack pointer.
  286.     mov    _Ext_Stack_Pointer,sp    ; set up the Scheme stack pointer
  287.     mova    _Registers,r6        ; set up the register array pointer
  288.     mov    _Free,r5        ; set up the free register
  289.     mov    regblock_val(r6),r0    ; set up the value register
  290.     and    &<address-mask>,r0,r4    ; set up the dynamic link register
  291.     jmp    0(r1)            ; go to compiled Scheme code
  292.  
  293. scheme_to_interface_jsr:
  294.     pop    r1            ; return address is first arg.
  295.     add    &4,r1,r1        ; bump past format word
  296.     jmp    scheme_to_interface
  297.  
  298. trampoline_to_interface:
  299.     pop    r1            ; return address is first arg.
  300.  
  301. scheme_to_interface:
  302.     mov    sp,_Ext_Stack_Pointer    ; update the C variables
  303.     mov    r5,_Free
  304.     mov    _saved_C_sp,sp
  305.     mov    0(sp),r6        ; restore C frame pointer
  306.     push    r4            ; push arguments to utility
  307.     push    r3
  308.     push    r2
  309.     push    r1
  310.     mova    _utility_table,r1
  311.     mul    &4,r0,r0        ; scale index to byte offset
  312.     add    r0,r1,r1
  313.     mov    0(r1),r1
  314.     jsr    0(r1)            ; invoke the utility
  315.  
  316.     add    &16,sp,sp        ; pop arguments to utility
  317.     mov    4(r0),r1        ; extract argument to return entry point
  318.     mov    0(r0),r0        ; extract return entry point
  319.     jmp    0(r0)            ; invoke it
  320.  
  321. _interface_to_C:
  322.     mov    r1,r0            ; C return value
  323.     pop    r6            ; restore frame pointer
  324.     pop    r5            ; and callee-saves registers
  325.     pop    r4
  326.     pop    r3
  327.     pop    r6            ; restore caller's frame pointer
  328.     rts                ; return to caller of C_to_interface    
  329.  
  330. Note that somewhere in the register array there would be a section
  331. with the following code:
  332.  
  333. offsi    jmp    scheme_to_interface
  334. offsj    jmp    scheme_to_interface_jsr
  335. offti    jmp    trampoline_to_interface
  336.     < perhaps more >
  337.  
  338. So that the compiler could issue the following code to invoke utilities:
  339.  
  340.     <set up arguments in r1 - r4>
  341.     mov    &<utility index>,r0
  342.     jmp    offsi(r6)
  343.  
  344. or
  345.  
  346.     <set up arguments in r2 - r4>
  347.     mov    &<utility index>,r0
  348.     jsr    offsj(r6)
  349.     <format word for the return address>
  350.  
  351. <label for the return address>
  352.     <more instructions>
  353.  
  354. Trampolines (created by the linker) would contain the code
  355.  
  356.     mov    &<trampoline utility>,r0
  357.     jsr    offti(r6)
  358.  
  359. 2 (RISC processor):
  360.  
  361. Machine M2 is a load-store architecture and has 31 general-purpose
  362. registers (R1 - R31), and R0 always holds 0.  There is no
  363. hardware-distinguished stack pointer.  The JL (jump and link)
  364. instruction transfers control to the target and leaves the address of
  365. the following instruction in R1.  The JLR (jump and link register)
  366. instruction is like JL but takes the target address from a register
  367. and an offset, rather than a field in the instruction.  There are no
  368. delay slots on branches, loads, etc. (to make matters simpler).
  369.  
  370. R2 is used as a software stack pointer, post-incremented to push.
  371.  
  372. The C calling convention is as follows:
  373.  
  374. - the return address is in r1 on entry.
  375. - there is no frame pointer.  Procedures allocate all the stack space
  376. they'll ever need (including space for callees' parameters) on entry.
  377. - all arguments (and the return address) have slots allocated on the
  378. stack, but the values of the first four arguments are passed on r3 -
  379. r6.  They need not be preserved accross nested calls.
  380. - scalar values are returned in r3.
  381. - structures of 4 words or less are returned in r3 - r6.
  382. - the stack is popped by the caller.
  383. - r7 - r10 are caller-saves registers.
  384. - r11 - r31 are callee-saves registers.
  385.  
  386. The Scheme register convention is as follows:
  387.  
  388. - register r7 holds the dynamic link, if present.
  389. - register r8 holds the register copy of MemTop.
  390. - register r9 holds the Free pointer.
  391. - register r10 holds the Scheme stack pointer.
  392. - register r11 holds the address of scheme_to_interface.
  393. - register r12 holds the address of the register block.
  394. - register r13 holds a mask to clear type-code bits from an object.
  395. - values are returned in r3.
  396. - the other registers are available without restrictions to the compiler.
  397.  
  398. Note that scheme_to_interface, the address of the register block, and
  399. the type-code mask, which are all constants have been assigned to
  400. callee-saves registers.  This guarantees that they will be preserved
  401. around utility calls and therefore interface_to_scheme need not set
  402. them again.
  403.  
  404. The utility calling convention is:
  405.  
  406. - arguments are placed in r3 - r6.
  407. - the index is placed in r14.
  408.  
  409. The argument registers are exactly those where the utility expects
  410. them.  In the code below, C_to_interface pre-allocates the frame for
  411. utilities called by scheme_to_interface, so scheme_to_interface has
  412. very little work to do.
  413.  
  414. The code would then be:
  415.  
  416. OFFSET is (21 + 1 + 4) * 4, the number of bytes allocated by
  417. _C_to_interface from the stack.  21 is the number of callee-saves
  418. registers to preserve.  1 is the return address for utilities, and 4
  419. is the number of arguments passed along.
  420.  
  421. _C_to_interface:
  422.     add    &OFFSET,r2,r2        ; allocate stack space
  423.  
  424.     st    r1,-4-OFFSET(r2)    ; save the return address
  425.     st    r11,0-OFFSET(r2)    ; save the callee-saves registers
  426.     st    r12,4-OFFSET(r2)
  427.     ...
  428.     st    r31,-24(r2)        ; -20 - -4 are for the utility.
  429.  
  430.     or    &mask,r0,r13        ; set up the pointer mask
  431.     lda    _Registers,r12        ; set up the register array pointer
  432.     jl    continue        ; get address of continue in r1
  433. continue
  434.     add    &(scheme_to_interface-continue),r1,r11
  435.     or    r0,r3,r4        ; preserve the entry point
  436.  
  437. _interface_to_scheme:
  438.     ld    _Ext_Stack_Pointer,r10    ; set up the Scheme stack pointer
  439.     ld    _Free,r9        ; set up the Free pointer
  440.     ld    _MemTop,r8        ; set up the Memory Top pointer
  441.     ld    regblock_val(r12),r3    ; set up the return value
  442.     and    r13,r3,r7        ;  and the dynamic link register
  443.     jlr    0(r4)            ; go to compiled Scheme code.
  444.  
  445.  
  446. scheme_to_interface_jl:
  447.     add    &4,r1,r1        ; bump past format word
  448.  
  449. trampoline_to_interface:
  450.     or    r0,r1,r3        ; return address is first arg.
  451.  
  452. scheme_to_interface:
  453.     st    r10,_Ext_Stack_Pointer    ; update the C variables
  454.     st    r9,_Free
  455.     mul    &4,r14,r14        ; scale utility index
  456.     lda    _utility_table,r8
  457.     add    r8,r14,r8
  458.     ld    0(r8),r8        ; extract utility's entry point
  459.     jlr    0(r8)
  460.  
  461.     jlr    0(r3)            ; invoke the assembly language entry point
  462.                     ;  on return from the utility.
  463.  
  464.  
  465. _interface_to_C:
  466.     or    r0,r4,r3        ; return value for C
  467.     ld    -24(r2),r31        ; restore the callee-saves registers
  468.     ...
  469.     ld    0-OFFSET(r2),r11
  470.     ld    -4-OFFSET(r2),r1    ; restore the return address
  471.     add    &-OFFSET,r2,r2
  472.     jlr    0(r1)            ; return to caller of C_to_interface
  473.     
  474.  
  475. Ordinary utilities would be invoked as follows:
  476.     
  477.     <set up arguments in r3 - r6>
  478.     or    &<utility index>,r0,r14
  479.     jlr    0(r11)
  480.  
  481. Utilities expecting a return address could be invoked as follows:
  482.  
  483.     <set up arguments in r4 - r6>
  484.     or    &<utility index>,r0,r14
  485.     jlr    -8(r11)
  486.  
  487. Trampolines would contain the following code:
  488.  
  489.     or    &<utility index>,r0,r14
  490.     jlr    -4(r11)
  491.