Annotation of researchv10no/cmd/sml/src/runtime/MIPS.prim.nw, revision 1.1.1.1

1.1       root        1: \section{Assembly-language primitives for the run-time system}
                      2: This file is derived from the similar file for the VAX.
                      3: We include [[<regdef.h>]], which defines names for the registers.
                      4: <<*>>=
                      5: #include "tags.h"
                      6: #include "prof.h"
                      7: #include "ml.h"
                      8: #include "prim.h"
                      9: <<register definitions>>
                     10: <<[[String]] and [[Closure]] definitions>>
                     11:        .data
                     12: <<data segment items>>
                     13:        .text
                     14: <<run vector>>
                     15: <<array>>
                     16: <<string and bytearray>>
                     17: <<C linkage>>
                     18: <<calling C routines>>
                     19: <<system calls>>
                     20: <<floating point>>
                     21: /* this bogosity is for export.c */
                     22:        .globl  startptr
                     23: startptr: .word    __start     /* just a guess... */
                     24: 
                     25: 
                     26: @ We define a couple of macros for creating strings and closures.
                     27: When calling [[String]] we should use a literal string whose length 
                     28: is a multiple of~4.
                     29: 
                     30: The closure of a primitive function
                     31:  is a record of length~1, containing a pointer to the first 
                     32: instruction in the function.
                     33: All closures have length~1 because there aren't any free variables in any 
                     34: of the primitive functions.
                     35: <<[[String]] and [[Closure]] definitions>>=
                     36: #define String(handle,len,str) .align 2;\
                     37:                               .set noreorder;\
                     38:                               .word len*power_tags+tag_string;\
                     39:                               handle: .ascii str;\
                     40:                               .set reorder
                     41: #define Closure(name) .align   2;\
                     42:                      .set noreorder;\
                     43:                      .word     mak_desc(1,tag_record);\
                     44:                      name:     .word 9f; /* address of routine */ \
                     45:                      .word     1; /* here for historical reasons */\
                     46:                      .word     tag_backptr;\
                     47:                      .set reorder;\
                     48:                      9:
                     49: @ \subsection{Allocation and garbage collection}
                     50: Put a brief summary here: gc is caused by storing beyond the end of high 
                     51: memory.
                     52: For that reason we store the last word of objects first.
                     53: 
                     54: @ \subsection{Register usage}
                     55: \input regs
                     56: <<register definitions>>=
                     57: #define stdarg 2
                     58: #define stdcont 3
                     59: #define stdclos 4
                     60: #define storeptr 22
                     61: #define dataptr 23
                     62: #define exnptr 30
                     63: #define artemp1 24
                     64: #define artemp2 25
                     65: #define artemp3 20
                     66: #define ptrtemp 21
                     67: @ The MIPS version of Unix doesn't put underscores in front of global names.
                     68: 
                     69: First we define the global [[runvec]].
                     70: This is an Ml object that represents the substructure [[A]] in 
                     71: {\tt boot/assembly.sig}.
                     72: All the ML functions will call these primitives by grabbing them
                     73: out of this record, which contains pointers to all the primitives.
                     74: <<run vector>>=
                     75: 
                     76:        .globl  runvec
                     77:        .align  2
                     78:         .word  mak_desc(8,tag_record)
                     79: runvec:
                     80:        .word   array_v
                     81:         .word  callc_v
                     82:        .word   create_b_v
                     83:        .word   create_s_v
                     84:        .word   floor_v
                     85:        .word   logb_v
                     86:        .word   scalb_v
                     87:        .word   syscall_v
                     88: 
                     89: @ \subsection{Creating arrays, strings, and bytearrays}
                     90: [[array(m,x)]] creates an array of length $n$, each element initialized
                     91: to $x$.
                     92: (The corresponding record creation code is not implemented as a primitive; 
                     93: the ML compiler generates that code in line.)
                     94: $n$~is a tagged integer representing the length in words; 
                     95: $x$ can be any value.
                     96: This routine will loop forever (or until something strange happens
                     97: in memory) if $n<0$.
                     98: 
                     99: We need to be careful in the implementation to make sure all register values 
                    100: are sensible when garbage collection might occur.
                    101: 
                    102: If the order of instructions seems a little strange, it's because we try to
                    103: make sensible use of load delay slots.
                    104: <<array>>=
                    105: Closure(array_v)
                    106:        lw $artemp1,0($stdarg)          /* tagged length in $artemp1 */
                    107:        lw $10,4($stdarg)               /* get initial value in $10 */
                    108:        sra $artemp1,1                  /* drop the tag bit */
                    109:        sll $artemp2,$artemp1,width_tags /* length for descr into $artemp2 */
                    110:        ori $artemp2,tag_array          /* complete descriptor into $artemp2 */
                    111:        sll $artemp1,2                  /* get length in bytes into $artemp1 */
                    112: .set noreorder /* can't reorder because collection might occur */
                    113:        add $artemp3,$artemp1,$dataptr  /* $artemp3 points to last word 
                    114:                                                        of new array*/
                    115: badgc1:        sw $0,($artemp3)                        /* clear; causes allocation */
                    116: .set reorder  /* can rearrange instructions again */
                    117: <<load bad pc into [[$artemp2]]; branch to [[badpc]] if == [[$artemp1]]>>=
                    118:        la $artemp2,badgc1
                    119:        beq $artemp1,$artemp2,badpc
                    120: @ At this point garbage collection may have occurred.
                    121: Ordinarily, we couldn't rely
                    122: on the value in [[$artemp3]], because it's not forwarded.
                    123: However, this is one of the special garbage collection locations, 
                    124: so we do know that [[$artemp3]] is sensible.
                    125: But, since we really want something larger, we recompute it, 
                    126: using the (possibly changed) value of the data pointer.
                    127: Extra cleverness here might enable us to save one instruction.
                    128: <<array>>=
                    129:        sw $artemp2,0($dataptr)         /* store the descriptor */
                    130:        add $dataptr,4                  /* points to new object */
                    131:        add $artemp3,$artemp1,$dataptr  /* beyond last word of new array*/
                    132:        add $stdarg,$dataptr,$0         /* put ptr in return register
                    133:                                        (return val = arg of continuation) */
                    134:        <<store the initial value in every slot, leaving [[$dataptr]] pointing to the first free word>>
                    135:        lw $10,0($stdcont)              /* grab continuation */
                    136:        j $10                           /* return */
                    137: 
                    138: @ With some clever thinking, the size of this loop could probably be cut 
                    139: from four instructions to three instructions.
                    140: <<store the initial value in every slot, leaving [[$dataptr]] pointing to the first free word>>=
                    141:        b 2f
                    142: 1:     sw $10,0($dataptr)              /* store the value */
                    143:        addi $dataptr,4                 /* on to the next word */
                    144: 2:     bne $dataptr,$artemp3,1b        /* if not off the end, repeat */
                    145: 
                    146: 
                    147: @ [[create_b(n)]] creates a byte-array of length $n$, and
                    148: [[create_s(n)]] creates a string of length $n$.
                    149: 
                    150: We use the same code to create byte arrays and strings, since the only
                    151: difference is in the tags.
                    152: The odd arrangement of closures (odd because each one starts a new record)
                    153: causes no problems because this code isn't in the garbage-collectible region.
                    154: <<string and bytearray>>=
                    155: Closure(create_b_v)
                    156:        addi $artemp3,$0,tag_bytearray  /* tag into $artemp3 */
                    157:        b       2f
                    158: Closure(create_s_v)
                    159:        addi $artemp3,$0,tag_string     /* tag into $artemp3 */
                    160: 2:     
                    161: @ The length computation may be a bit confusing.
                    162: We are handed a tagged integer $2n+1$, and we need to compute the required
                    163: number of words, which is $\lfloor{n+3\over 4}\rfloor$.
                    164: This is just $\lfloor{(2n+1)+5\over 8}\rfloor$.
                    165: However, we'll save an instruction later if we happen to have one more than 
                    166: the number of words tucked away in a register, 
                    167: because $1+\lfloor{n+3\over 4}\rfloor$ is the number of words
                    168: we're taking from the data space (we include the descriptor).
                    169: So we compute $(2n+1)+13$ and continue accordingly
                    170: <<string and bytearray>>=
                    171:        addi    $artemp1,$stdarg,13     /* $2n+14$ */
                    172:        sra     $artemp1,3              /* number of words in string+tag */
                    173:        sll     $artemp1,2              /* # of bytes allocated for str+tag */
                    174: .set noreorder /* don't cross gc boundary */
                    175:        add     $artemp2,$artemp1,$dataptr /* beyond last word of string */
                    176: badgc2:        sw $0,-4($artemp2)              /* clear last; causes allocation */
                    177: .set reorder
                    178:        sra     $artemp2,$stdarg,1      /* untagged length in bytes */
                    179:        sll     $artemp2,width_tags     /* room for descriptor */
                    180:        or      $artemp2,$artemp3       /* descriptor */
                    181:        sw      $artemp2,0($dataptr)    /* store descriptor */
                    182:        addi    $stdarg,$dataptr,4      /* pointer to new string */
                    183:        add     $dataptr,$artemp1       /* advance; save 1 instruction */
                    184:        lw $10,0($stdcont)              /* grab continuation */
                    185:        j $10                           /* return */
                    186: 
                    187: <<load bad pc into [[$artemp2]]; branch to [[badpc]] if == [[$artemp1]]>>=
                    188:        la $artemp2,badgc2
                    189:        beq $artemp1,$artemp2,badpc
                    190: 
                    191: @ \subsection{Linkage with C code}
                    192: C always gains control first, and stuffs something appropriate into
                    193: the register save areas before starting ML by calling [[restoreregs]].
                    194: It also puts something appropriate in the ML [[saved_pc]].
                    195: [[restoreregs]] squirrels away the current state of the C runtime stack,
                    196: restores the ML registers, and finally jumps to the saved program counter.
                    197: 
                    198: When ML wants to call C, it calls [[saveregs]], which saves the ML state 
                    199: in the appropriate save areas, then restores the C runtime stack and returns.
                    200: Before returning to C, it sets [[cause]] to something appropriate.
                    201: 
                    202: All programs must ensure that [[restoreregs]] {\em never} calls itself
                    203: recursively, because it is {\em not} reentrant.
                    204: 
                    205: The C end of this connection is on display in the [[runML()]] function of
                    206: [[~ml/src/runtime/callgc.c]].
                    207: <<data segment items>>=
                    208: bottom: .word 0                        /* C's saved stack pointer */
                    209: <<C linkage>>=
                    210:        .globl saveregs
                    211:        .globl handle_c
                    212:        .globl return_c
                    213:        .globl restoreregs
                    214: .ent restoreregs
                    215: restoreregs:
                    216: <<save caller's stuff using MIPS calling conventions>>
                    217: <<enable floating point overflow and zerodivide exceptions>>
                    218:        sw      $sp,bottom      /* save C's stack pointer */
                    219: <<if [[saved_pc]] points to a bad spot, adjust it (destroys arithmetic temps)>>
                    220: <<restore the ML registers>>
                    221: .set noat /* This trick will cause a warning, but the code is OK */
                    222:        lw      $at,saved_pc    /* grab the saved program counter */
                    223:        j       $at             /* and continue executing at that spot */
                    224: .set at
                    225: 
                    226: @ The next two functions are an exception handler and a continuation for
                    227: ML programs called from C.
                    228: Although neither appears to return any result (by manipulating [[$stdarg]],
                    229: they do return results.  
                    230: It's just that the C code on the other end gets the result out of 
                    231: [[saved_ptrs[0],]] where it expects to find [[$stdarg]].
                    232: <<C linkage>>=
                    233: Closure(handle_c) /* exception handler for ML functions called from C */
                    234:        li      $artemp1,CAUSE_EXN
                    235:        sw      $artemp1,cause
                    236:        b       saveregs
                    237: Closure(return_c) /* continuation for ML functions called from C */
                    238:        li      $artemp1,CAUSE_RET
                    239:        sw      $artemp1,cause
                    240: saveregs: 
                    241: <<save the ML registers>>
                    242:        lw      $sp,bottom      /* recover C's stack pointer */
                    243: <<restore caller's stuff using MIPS calling conventions>>
                    244:        j       $31     /* return to C program */
                    245: .end restoreregs
                    246: 
                    247: <<enable floating point overflow and zerodivide exceptions>>=
                    248: .set noat
                    249:        cfc1 $at,$31            /* grab fpa control register */
                    250:        ori  $at,$at,0x600      /* set O and Z bits */
                    251:        ctc1 $at,$31            /* return fpa control register */
                    252: .set at
                    253: 
                    254: @ The MIPS calling conventions are described in gory detail in Appendix~D
                    255: of the MIPS book; pages D-18 and following.
                    256: 
                    257: At the moment we don't save any floating point registers.
                    258: We save (on the stack) nine general-purpose registers, the global pointer,
                    259:  and the return address.
                    260: 
                    261: We always have to allocate at least 16 bytes for argument build,
                    262: because any C function might be varargs, and might begin by
                    263: spilling all of its registers into the argument build area (Hanson).
                    264: We allocate exactly sixteen bytes, planning to fiddle the stack if
                    265: (God forbid) we are ever asked to issue a system call with more than
                    266: 16 bytes worth of arguments.
                    267: 
                    268: <<save caller's stuff using MIPS calling conventions>>=
                    269: #define regspace 44
                    270: #define localspace 4
                    271: #define argbuild 16
                    272: #define framesize (regspace+localspace+argbuild) /* must be multiple of 8 */
                    273: #define frameoffset (0-localspace)
                    274:        subu $sp,framesize
                    275: <<give .mask and save the C registers>>
                    276: 
                    277: <<restore caller's stuff using MIPS calling conventions>>=
                    278: <<restore the C registers>>
                    279:        addu $sp,framesize
                    280: @ We don't save floating point regs yet.
                    281: <<give .mask and save the C registers>>=
                    282: .mask 0xd0ff0000,0-localspace
                    283:        sw      $31,argbuild+40($sp)
                    284:        sw      $30,argbuild+36($sp)
                    285:        sw      $gp,argbuild+32($sp)
                    286:         sw      $23,argbuild+28($sp)
                    287:         sw      $22,argbuild+24($sp)
                    288:         sw      $21,argbuild+20($sp)
                    289:         sw      $20,argbuild+16($sp)
                    290:         sw      $19,argbuild+12($sp)
                    291:         sw      $18,argbuild+8($sp)
                    292:         sw      $17,argbuild+4($sp)
                    293:         sw      $16,argbuild($sp)
                    294: <<restore the C registers>>=
                    295:        lw      $31,argbuild+40($sp)
                    296:        lw      $30,argbuild+36($sp)
                    297:        lw      $gp,argbuild+32($sp)
                    298:         lw      $23,argbuild+28($sp)
                    299:         lw      $22,argbuild+24($sp)
                    300:         lw      $21,argbuild+20($sp)
                    301:         lw      $20,argbuild+16($sp)
                    302:         lw      $19,argbuild+12($sp)
                    303:         lw      $18,argbuild+8($sp)
                    304:         lw      $17,argbuild+4($sp)
                    305:         lw      $16,argbuild($sp)
                    306: 
                    307: 
                    308: @ There are two save areas; one for pointers and one for non-pointers.
                    309: (The pointer area may, of course, include tagged integers.)
                    310: The pointer area has special spots for standard argument, continuation,
                    311: and closure.
                    312: In addition there are special save areas for the special registers.
                    313: Register 31 is to be maintained constant relative to the program counter,
                    314: so we store the difference with [[saved_pc]].
                    315: <<save the ML registers>>=
                    316:                                        /* needn't save $1 */
                    317:        /* the big three: argument, continuation, closure */
                    318:        sw      $stdarg,saved_ptrs
                    319:        sw      $stdcont,saved_ptrs+4
                    320:        sw      $stdclos,saved_ptrs+8
                    321:        
                    322:        /* All the miscellaneous guys */
                    323:         sw      $5,saved_ptrs+12
                    324:         sw      $6,saved_ptrs+16
                    325:         sw      $7,saved_ptrs+20
                    326:         sw      $8,saved_ptrs+24
                    327:         sw      $9,saved_ptrs+28
                    328:         sw      $10,saved_ptrs+32
                    329:         sw      $11,saved_ptrs+36
                    330:         sw      $12,saved_ptrs+40
                    331:         sw      $13,saved_ptrs+44
                    332:         sw      $14,saved_ptrs+48
                    333:         sw      $15,saved_ptrs+52
                    334:         sw      $16,saved_ptrs+56
                    335:         sw      $17,saved_ptrs+60
                    336:         sw      $18,saved_ptrs+64
                    337:         sw      $19,saved_ptrs+68
                    338: 
                    339:        sw      $21, saved_ptrs+72
                    340: 
                    341:        sw      $artemp1,saved_nonptrs
                    342:        sw      $artemp2,saved_nonptrs+4
                    343:        sw      $artemp3,saved_nonptrs+8
                    344: 
                    345:        /* don't touch registers $26 and $27 */
                    346: 
                    347:        sw      $storeptr,saved_storeptr
                    348:        sw      $dataptr,saved_dataptr
                    349:        sw      $exnptr,saved_exnptr
                    350: 
                    351: <<save $[[$31]]-[[saved_pc]]$ in [[saved_pc_diff]] (destroys [[$artemp1]])>>
                    352: 
                    353: 
                    354: <<restore the ML registers>>=
                    355:        /* the big three: argument, continuation, closure */
                    356:        lw      $stdarg,saved_ptrs
                    357:        lw      $stdcont,saved_ptrs+4
                    358:        lw      $stdclos,saved_ptrs+8
                    359:        
                    360:        /* All the miscellaneous guys */
                    361:         lw      $5,saved_ptrs+12
                    362:         lw      $6,saved_ptrs+16
                    363:         lw      $7,saved_ptrs+20
                    364:         lw      $8,saved_ptrs+24
                    365:         lw      $9,saved_ptrs+28
                    366:         lw      $10,saved_ptrs+32
                    367:         lw      $11,saved_ptrs+36
                    368:         lw      $12,saved_ptrs+40
                    369:         lw      $13,saved_ptrs+44
                    370:         lw      $14,saved_ptrs+48
                    371:         lw      $15,saved_ptrs+52
                    372:         lw      $16,saved_ptrs+56
                    373:         lw      $17,saved_ptrs+60
                    374:         lw      $18,saved_ptrs+64
                    375:         lw      $19,saved_ptrs+68
                    376: 
                    377:        lw      $21, saved_ptrs+72
                    378: 
                    379: <<restore [[$31]] from [[saved_pc]] \& [[saved_pc_diff]] (destroys [[$artemp1]])>>
                    380:        lw      $artemp1,saved_nonptrs
                    381:        lw      $artemp2,saved_nonptrs+4
                    382:        lw      $artemp3,saved_nonptrs+8
                    383: 
                    384:        /* don't touch registers $26 and $27 */
                    385: 
                    386:        lw      $storeptr,saved_storeptr
                    387:        lw      $dataptr,saved_dataptr
                    388:        lw      $exnptr,saved_exnptr
                    389: 
                    390: <<save $[[$31]]-[[saved_pc]]$ in [[saved_pc_diff]] (destroys [[$artemp1]])>>=
                    391:        lw $artemp1,saved_pc
                    392:        subu $artemp1,$31,$artemp1      /* mustn't overflow */
                    393:        sw $artemp1,saved_pc_diff
                    394: <<restore [[$31]] from [[saved_pc]] \& [[saved_pc_diff]] (destroys [[$artemp1]])>>=
                    395:        lw $artemp1,saved_pc
                    396:        lw $31,saved_pc_diff
                    397:        addu $31,$artemp1               /* mustn't overflow */
                    398: <<data segment items>>=
                    399: saved_pc_diff: .word 0
                    400: 
                    401: 
                    402: @ Because the Mips has no indexed addressing modes, there are special
                    403: circumstances under which we have to adjust the program counter before
                    404: a garbage collection.
                    405: The problem arises when we want to create an object whose size is not
                    406: known at compile time.
                    407: In order to do that, we have to add the size of the object to [[$dataptr]],
                    408: putting the result in a new register.
                    409: We then store at offset $-4$ from that register to allocate (and possibly
                    410: cause garbage collection).
                    411: That register can't be a pointer, because at the time of the gc it doesn't
                    412: point to anything sensible (in fact, by definition it points out of the
                    413: garbage-collectible region entirely).
                    414: If it is a nonpointer, though, it isn't changed by the garbage collection,
                    415: so when the collection is over, we attempt once again to store in exactly
                    416: the same place, causing another fault (unless the heap has been resized).
                    417: 
                    418: The solution is a hack.  Since there are only two places this problem
                    419: can occur, we check [[saved_pc]] against the offending program counters.
                    420: If we find one, we reduce [[saved_pc]] by 4 (the size of one instruction),
                    421: causing the addition to be repeated.
                    422: 
                    423: <<if [[saved_pc]] points to a bad spot, adjust it (destroys arithmetic temps)>>=
                    424:     lw $artemp1,saved_pc
                    425:     <<load bad pc into [[$artemp2]]; branch to [[badpc]] if == [[$artemp1]]>>
                    426:     b 1f
                    427: badpc:
                    428:     subu $artemp1,4            /* adjust */
                    429:     sw $artemp1,saved_pc       /* save */
                    430: 1:
                    431: 
                    432: 
                    433: @ [[callc(f,a)]] calls a C-language function [[f]] with argument [[a]].
                    434: We don't have to save a register unless we'll need its value later.
                    435: 
                    436: The closure of this routine is irrelevant, since [[callc]] doesn't
                    437: have any free variables.
                    438: Therefore the only things that have to be restored after the call to~C
                    439: are the continuation, the store pointer, the data pointer, and
                    440: the exception handler.
                    441: If we wanted [[callc]] to be more efficient, we would
                    442: rearrange things so that all those registers fell into [[s0]]--[[s8]],
                    443: where they would automatically be preserved across procedure calls.
                    444: As it stands, everything except the continuation is preserved,
                    445: so we're not doing too badly.
                    446: 
                    447: Miraculously, C routines return integer results in [[$2]], which is
                    448: exactly the register we need to pass to our continuation (in order to
                    449: return a value).
                    450: I decided not to rely on this, and to include a [[move]] instruction
                    451: anyway.  Maybe the assembler will park it in a delay slot since it
                    452: is a nop.
                    453: <<calling C routines>>=
                    454: Closure(callc_v)
                    455:        sw $stdcont,argbuild+regspace($sp) /* save continuation on stack */
                    456:        lw $4,4($stdarg)        /* get value a into arg register */
                    457:        lw $10,0($stdarg)       /* get address of f into misc reg */
                    458:        jal $10                 /* call f ($31 can be trashed) */
                    459:        move $stdarg,$2         /* return val is argument to continuation */
                    460:        lw $stdcont,argbuild+regspace($sp) /* recover continuation */
                    461: <<put zeroes in all forwardable regs that might hold garbage>>
                    462:        lw $artemp3,cause       /* get cause */
                    463:         bne $artemp3,$0,saveregs /* if cause != 0, save ML & return to C */
                    464:        lw $10,0($stdcont)      /* grab continuation */
                    465:        j $10                   /* return */
                    466: @ A forwardable register can hold garbage unless it was saved
                    467: by C (is in [[s0]]--[[s8]]) or is [[$stdarg]] of [[$stdcont]].
                    468: <<put zeroes in all forwardable regs that might hold garbage>>=
                    469:        move $stdclos,$0
                    470:        move $5,$0
                    471:        move $6,$0
                    472:        move $7,$0
                    473:        move $8,$0
                    474:        move $9,$0
                    475:        move $10,$0
                    476:        move $11,$0
                    477:        move $12,$0
                    478:        move $13,$0
                    479:        move $14,$0
                    480:        move $15,$0
                    481:        /* $16--$23 and $30 are saved by the callee */
                    482: 
                    483: @ This interface is going to be agony, because the rules for passing
                    484: arguments are passing strange.
                    485: The interface is [[syscall_v(callnumber,argvector,argcount)]].
                    486: 
                    487: The system call interface is the same as the procedure call interface,
                    488: but instead of a [[jal]] we use a [[syscall]] instruction, and
                    489: we put the system call number in register [[$2]].
                    490: It appears that, after the execution of the [[syscall]] handler,
                    491: the result is in [[$2]], and [[$7]] is zero unless an error occurred.
                    492: We will put all the arguments on the stack, then load the first four
                    493: into [[$4]]--[[$7]].
                    494: <<system calls>>=
                    495: Closure(syscall_v)
                    496:        sw $stdcont,argbuild+regspace($sp) /* save continuation on stack */
                    497:        lw $artemp1,8($stdarg)  /* 2*argc+1 in $artemp1 */
                    498:        sra $artemp1,1          /* argc in $artemp1 */
                    499:        move $16,$sp            /* save our $sp */
                    500: <<extend argbuild area to be big enough for all arguments>>
                    501:        lw $ptrtemp,4($stdarg)  /* argv in $ptrtemp */
                    502: <<put all arguments onto the stack>>
                    503: <<load first four arguments into [[$4]]--[[$7]]>>
                    504: 9:     lw $2,0($stdarg)        /* get syscall # in $2; trash $stdarg */
                    505:        sra $2,1                /* throw out the tag bit */
                    506:        syscall
                    507:        move $sp,$16            /* recover the good stack pointer */
                    508:        lw $stdcont,argbuild+regspace($sp) /* recover continuation */
                    509:        bnez $7,1f              /* if error, return ~1 */
                    510:        move $stdarg,$2         /* return val is argument to continuation */
                    511:        add $stdarg,$stdarg     /* double return value */
                    512:        addi $stdarg,1          /* and add tag bit */
                    513:        b 2f
                    514: 1:     sw  $stdarg,errno
                    515:        li $stdarg,-1
                    516: 2:
                    517: <<put zeroes in all forwardable regs that might hold garbage>>
                    518:        lw $10,0($stdcont)      /* grab continuation */
                    519:        j $10                   /* return */
                    520: 
                    521: @ At this point we know that the number of arguments is in [[$artemp1]].
                    522: We have room for four arguments; if there are more 
                    523: we'll have to increase the stack size by the appropriate multiple of 8
                    524: so that it stays doubleword-aligned.
                    525: <<extend argbuild area to be big enough for all arguments>>=
                    526:        ble $artemp1,4,1f               /* big enough */
                    527:        sub $artemp2,$artemp1,3         /* (temp2 = argc - 4 + 1) > 1 */
                    528:        sra $artemp2,1                  
                    529:        sll $artemp2,3                  /* temp2 = 4 * roundup (argc-4,2) */
                    530:        subu $sp,$artemp2               /* increase stack */
                    531: 1:
                    532: 
                    533: @ Now we have a list of arguments pointed to by [[$ptrtemp]].
                    534: We have the count of the arguments in [[$artemp1]].
                    535: We have to put them on the stack.
                    536: We have to remove tag bits where appropriate.
                    537: <<put all arguments onto the stack>>=
                    538:        move $artemp2,$sp               /* destination in $artemp2 */
                    539:        b 1f                            /* branch forward to test */
                    540: 2:     /* argc > 0 */
                    541:        lw $artemp3,0($ptrtemp)         /* get list element */
                    542:        andi $10,$artemp3,1             /* tagged? */
                    543:        beqz $10,3f
                    544:        sra $artemp3,1                  /* drop tag bit */
                    545: 3:     sw $artemp3,0($artemp2)         /* save the argument */
                    546:        lw $ptrtemp,4($ptrtemp)         /* next element */
                    547:        add $artemp2,4                  /* next arg build area */
                    548:        sub $artemp1,1                  /* --argc */
                    549: 1:     bgtz $artemp1,2b                /* if argc>0, store another */
                    550: 
                    551: @ It doesn't matter if we load arguments that aren't there; the
                    552: system call will just ignore them.
                    553: <<load first four arguments into [[$4]]--[[$7]]>>=
                    554:        lw $4,0($sp)
                    555:        lw $5,4($sp)
                    556:        lw $6,8($sp)
                    557:        lw $7,12($sp)
                    558: 
                    559: @ \subsection{Floating point}
                    560: We store floating point constants in two words, with the least significant
                    561: word first.  
                    562: We use the 64 bit IEEE format.
                    563: 
                    564: We begin with instructions to change the rounding modes.
                    565: See the MIPS book, pages 6-5--6-7.
                    566: <<tell the floating point unit to round toward $-\infty$>>=
                    567: .set noat
                    568:        cfc1 $at,$31            /* grab fpa control register */
                    569:        ori  $at,0x03           /* set rounding bits to 11 */
                    570:        ctc1 $at,$31            /* return fpa control register */
                    571: .set at
                    572: <<tell the floating point unit to round to nearest>>=
                    573: .set noat
                    574:        cfc1 $at,$31            /* grab fpa control register */
                    575:        ori  $at,0x03           /* set rounding bits to 11 */
                    576:        xori $at,0x03           /* set rounding bits to 00
                    577:        ctc1 $at,$31            /* return fpa control register */
                    578: .set at
                    579: @ These floating point functions are used int floating to integer conversion.
                    580: <<floating point>>=
                    581: /* Floating exceptions raised (assuming ROP's are never passed to functions):
                    582:  *     DIVIDE BY ZERO - (div)
                    583:  *     OVERFLOW/UNDERFLOW - (add,div,sub,mul) as appropriate
                    584:  *
                    585:  * floor raises integer overflow if the float is out of 32-bit range,
                    586:  * so the float is tested before conversion, to make sure it is in (31-bit)
                    587:  * range */
                    588: 
                    589: @ [[floor(x)]] returns the smallest integer less than or equal to [[x]].
                    590: <<floating point>>=
                    591: Closure(floor_v)
                    592:        lwc1 $f4,0($stdarg)             /* get least significant word */
                    593:        lwc1 $f5,4($stdarg)             /* get most significant word */
                    594: <<tell the floating point unit to round toward $-\infty$>>
                    595:        cvt.w.d $f6,$f4                 /* convert to integer */
                    596: <<tell the floating point unit to round to nearest>>
                    597:        mfc1 $stdarg,$f6                /* get in std argument register */
                    598:        sll $stdarg,1           /* make room for tag bit */
                    599:        add $stdarg,1           /* add the tag bit */
                    600:        lw $10,0($stdcont)      /* grab continuation */
                    601:        j $10                   /* return */
                    602: 
                    603: 
                    604: @ [[logb(x)]] returns the exponent part of the floating point [[x]].
                    605: We grab the 11-bit exponent from the word, then unbias it (according
                    606: to the IEEE standard) by subtracting 1023.
                    607: <<floating point>>=
                    608: Closure(logb_v)
                    609:        lw      $stdarg,4($stdarg)      /* most significant part */
                    610:        srl     $stdarg,20              /* throw out 20 low bits */
                    611:        andi    $stdarg,0x07ff          /* clear all but 11 low bits */
                    612:        sub     $stdarg,1023            /* subtract 1023 */
                    613:        sll     $stdarg,1               /* make room for tag bit */
                    614:        add     $stdarg,1               /* add the tag bit */
                    615:        lw      $10,0($stdcont)         /* grab continuation */
                    616:        j       $10                     /* return */
                    617: 
                    618: @ [[scalb(x,n)]] adds [[n]] to the exponent of floating
                    619: point [[x]].
                    620: Since we don't want the resulting float to be anything
                    621: special, we insist that the unbiased exponent of the result
                    622: satisfy $-1022 \le E \le 1023$, i.e.\ that the biased exponent satisfy
                    623:  $1 \le e \le 2046$.
                    624: <<floating point>>=
                    625: Closure(scalb_v)
                    626:        lw      $artemp1,4($stdarg)     /* get tagged n */
                    627:        sra     $artemp1,1              /* get real n */
                    628:        beqz    $artemp1,9f             /* if zero, return the old float */
                    629:        lw      $ptrtemp,0($stdarg)     /* get pointer to float */
                    630:        lw      $artemp2,4($ptrtemp)    /* most significant part */
                    631:        srl     $artemp2,20             /* throw out 20 low bits */
                    632:        andi    $artemp2,0x07ff         /* clear all but 11 low bits */
                    633:        add     $artemp3,$artemp2,$artemp1      /* new := old + n */
                    634:        blt     $artemp3,1,under        /* punt if underflow */
                    635:        bgt     $artemp3,2046,over      /* or overflow */
                    636: <<allocate and store new floating point constant and set [[$stdarg]]>>
                    637:        lw      $10,0($stdcont)         /* grab continuation */
                    638:        j       $10                     /* return */
                    639: 
                    640: 9:     lw      $stdarg,0($stdarg)      /* get old float */
                    641:        lw      $10,0($stdcont)         /* grab continuation */
                    642:        j       $10                     /* return */
                    643: 
                    644: over:  la      $stdarg,1f              /* exception name in $stdarg */
                    645:        b       raise_real
                    646: String(1,8,"overflow")
                    647: under: la      $stdarg,1f              /* exception name in $stdarg */
                    648:        b       raise_real
                    649: String(1,9,"underflow\0\0\0")
                    650: 
                    651: raise_real:
                    652:  /* build new record to pass to exception handler */
                    653:  /*    [descriptor]
                    654:  /*    [exception (string)]
                    655:  /*    [real_e (more exception info)]
                    656:   */
                    657:        la      $10,real_e              /* get address of real_e */
                    658: .set noreorder
                    659:        sw      $10,8($dataptr)         /* allocate; may cause gc */
                    660: .set reorder
                    661:        sw      $stdarg,4($dataptr)
                    662:        li      $10,mak_desc(2,tag_record)
                    663:        sw      $10,0($dataptr)
                    664:        add     $stdarg,$dataptr,4      /* new record is argument */
                    665:        addi    $dataptr,12             /* $dataptr restored */
                    666:        move    $stdclos,$exnptr        /* make sure closure is right */
                    667:        lw      $10,0($exnptr)          /* grab handler */
                    668:        j       $10                     /* raise the exception */
                    669: 
                    670: @ Here we indulge in a little cleverness to save a couple of instructions.
                    671: Since the old value is in [[$artemp2]] and the new in [[$artemp3]],
                    672: we can [[xor]] them, then store the new one with a second [[xor]].
                    673: <<allocate and store new floating point constant and set [[$stdarg]]>>=
                    674:        xor     $artemp3,$artemp2       /* at3 = new xor old */
                    675:        sll     $artemp3,20             /* put exponent in right position */
                    676:        lw      $artemp2,4($ptrtemp)    /* most significant word */
                    677:        xor     $artemp2,$artemp3       /* change to new exponent */
                    678: .set noreorder
                    679:        sw      $artemp2,8($dataptr)    /* allocate; may cause gc */
                    680: .set reorder
                    681:        lw      $artemp2,0($ptrtemp)    /* get least significant word */
                    682:        li      $10,mak_desc(8,tag_string) /* make descriptor */
                    683:        sw      $artemp2,4($dataptr)    /* save lsw */
                    684:        sw      $10,0($dataptr)         /* save descriptor */
                    685:        add     $stdarg,$dataptr,4      /* get pointer to new float */
                    686:        add     $dataptr,12             /* point to new free word */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.