|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.