|
|
1.1 root 1: /* Copyright 1989 by AT&T Bell Laboratories */
2: /* SPARC.prim.s
3: *
4: * J.H. Reppy
5: * Cornell University
6: * Ithaca, NY 14853
7: * [email protected]
8: *
9: * HISTORY:
10: * 03/15/89 created
11: * 11/20/89 revised for to use heap limit check.
12: *
13: * SPARC runtime code for ML. Registers are used as follows:
14: *
15: * %g7 : exception handler continuation
16: * %g6 : freespace pointer
17: * %g5 : store pointer
18: * %g4 : heap limit pointer
19: *
20: * %i2 = closure; can be ignored because contains no free vars
21: * %i0 = arg
22: * %i1 = continuation
23: * %i3 = base code pointer
24: *
25: * %o0,%o1 = arith. temps
26: * %g1-%g3,%l0-%l7,%i4-%i5 = misc. registers (contain only pointers or tagged ints)
27: * %o5 = pointer temp.
28: *
29: * %o2,%o3 = used for args to ml_mul & ml_div, also used as temp registers
30: * %o4,%o5 = temp registers
31: *
32: * %o6 = %sp (not used by ML)
33: * %i6 = %fp (not used by ML)
34: * %i7 = return address to C code (not used by ML)
35: * %o7 = not used
36: *
37: * There are four places in this file where garbage collection can be triggered:
38: * in array_v, create_s_v, scalb_v and in raise_real. It it important that only
39: * registers saved by _saveregs be live at these points. Also, only pointer
40: * registers can contain heap pointers at these points.
41: */
42:
43: #include <machine/asm_linkage.h>
44: #include <machine/trap.h>
45: #include "tags.h"
46: #include "prof.h"
47: #include "prim.h"
48:
49: /* Macros to fetch and store values in memory; use %o5 as an addressing register. */
50: #define FETCH(addr, reg) \
51: sethi %hi(addr),%o3; \
52: ld [%o3+%lo(addr)],reg
53: #define STORE(reg, addr) \
54: sethi %hi(addr),%o3; \
55: st reg,[%o3+%lo(addr)]
56:
57:
58: /* Macros to save and restore the global registers. We save %g1-%g7 in the
59: * corresponding locals of the new window. The C-routine/system call is
60: * guaranteed not to touch the local or in registers (which are the out registers
61: * of the ML window).
62: */
63: #define SAVE_ML_REGS \
64: mov %g1,%l1; \
65: mov %g2,%l2; \
66: mov %g3,%l3; \
67: mov %g4,%l4; \
68: mov %g5,%l5; \
69: mov %g6,%l6; \
70: mov %g7,%l7
71:
72: #define RESTORE_ML_REGS \
73: mov %l1,%g1; \
74: mov %l2,%g2; \
75: mov %l3,%g3; \
76: mov %l4,%g4; \
77: mov %l5,%g5; \
78: mov %l6,%g6; \
79: mov %l7,%g7
80:
81: /* The ML stack frame has the following layout (set up by restoreregs):
82: *
83: * +-----------------+
84: * %fp = %sp+80: | temp for floor |
85: * +-----------------+
86: * %sp+76: | addr of _ml_div |
87: * +-----------------+
88: * %sp+72: | addr of _ml_mul |
89: * +-----------------+
90: * %sp+68: | saved %g6 |
91: * +-----------------+
92: * %sp+64: | saved %g7 |
93: * +-----------------+
94: * | space to save |
95: * | in and local |
96: * %sp: | registers |
97: * +-----------------+
98: *
99: * the size of the frame is
100: */
101: #define ML_FRAMESIZE (WINDOWSIZE+20)
102:
103:
104: #define String(handle,len,str) \
105: .align 4; \
106: .word len*power_tags+tag_string; \
107: handle: .ascii str; \
108: .align 4
109:
110: #define Closure(name) \
111: .align 4; \
112: .word mak_desc(1,tag_record); \
113: name: .word 7f; \
114: .word 1; \
115: .word tag_backptr; \
116: 7:
117:
118: #define RAISE \
119: ld [%g7],%i2; \
120: jmp %i2; \
121: mov %g7,%i1 /* (delay slot) */
122:
123: #define CONTINUE \
124: ld [%i1],%i2; \
125: jmp %i2; \
126: nop
127:
128:
129: .seg "text"
130: .global _runvec
131: .align 4
132: .word mak_desc(8, tag_record)
133: _runvec:
134: .word _array_v
135: .word _callc_v
136: .word _create_b_v
137: .word _create_s_v
138: .word _floor_v
139: .word _logb_v
140: .word _scalb_v
141: .word _syscall_v
142:
143:
144: .global _startprim
145: _startprim:
146:
147:
148: /* array : (int * 'a) -> 'a array
149: * Allocate and initialize a new array. This can cause GC.
150: */
151: Closure(_array_v)
152: 1:
153: ld [%i0],%o0 /* get length into %o0 */
154: ld [%i0+4],%l0 /* get default into %l0 */
155: sra %o0,1,%o0 /* convert to sparc int */
156: sll %o0,width_tags,%o1 /* build the tag in %o1 */
157: or %o1,tag_array,%o1
158: sll %o0,2,%o0 /* scale length to bytes */
159: add %g4,%o0,%o4 /* check the heap limit */
160: addcc %g6,%o4,%g0
161: bvs 3f /* we hit the limit, so trap safely */
162: dec 4,%o0 /* length-- */
163: st %o1,[%g6-4] /* store the tag */
164: mov %g6,%i0 /* result := object addr. */
165: 2: /* initialization loop */
166: st %l0,[%g6] /* store default. */
167: deccc 4,%o0 /* length-- */
168: bge 2b
169: inc 4,%g6 /* freeptr++ (delay slot) */
170: /* end loop */
171: inc 4,%g6 /* freeptr++ */
172: CONTINUE
173: 3: /* we come here to do a safe GC trap. */
174: add %g0,0,%g0 /* a nop to get PC adjust right */
175: taddcctv %g6,%o4,%g0 /* cause the GC trap. */
176: ba 1b /* retry the allocation. */
177: nop
178:
179:
180: /* create_b : int -> string
181: * create_s : int -> string
182: * Create bytearray or string of given length. This can cause GC.
183: */
184: Closure(_create_b_v)
185: ba 2f
186: mov tag_bytearray,%l1 /* (delay slot) */
187:
188: Closure(_create_s_v)
189: mov tag_string,%l1
190:
191: 2: sra %i0,1,%o0 /* %o0 = length (sparc int) */
192: sll %o0,width_tags,%o2
193: or %o2,%l1,%o1 /* build the tag in %o1 */
194: add %o0,3,%o0 /* %o0 = length in words (no tag) */
195: sra %o0,2,%o0
196: sll %o0,2,%o0 /* %o0 = length in bytes (no tag) */
197: add %g4,%o0,%o4 /* Check the heap limit. */
198: addcc %g6,%o4,%g0
199: bvs 3f /* we hit the limit, so trap safely. */
200: st %o1,[%g6-4] /* store the tag */
201: mov %g6,%i0 /* result := object addr */
202: add %o0,4,%o0 /* %o0 = length in bytes (including tag) */
203: add %o0,%g6,%g6 /* freeptr += length */
204: CONTINUE
205: 3: /* we come here to do a safe GC trap. */
206: add %g0,0,%g0 /* a nop to get PC adjust right */
207: taddcctv %g6,%o4,%g0 /* cause the GC trap. */
208: ba 2b /* retry the allocation. */
209: nop
210:
211:
212: /* floor : real -> int
213: * Return the floor of the argument or else raise Float("floor") if out of range.
214: * We implement the range check by using an integer comparison with the high 32
215: * bits of the real value (which contains the biased exponent).
216: * (double)(2^30) == [0x41d00000, 0x0]
217: * (double)(-2^30) == [0xc1d00000, 0x0]
218: */
219: Closure(_floor_v)
220: ld [%i0],%f0 /* fetch arg into %f0, %f1. */
221: ld [%i0+4],%f1
222: ld [%i0],%o0 /* %o0 gets high word. */
223: tst %o0 /* negative ? */
224: blt 1f
225: nop
226: /* handle positive case */
227: set 0x41d00000,%o1 /* %o1 = 2^30 */
228: cmp %o0,%o1 /* if %o0 >= 2^30 then range error */
229: bge out_of_range
230: nop
231: fdtoi %f0,%f2 /* cvt to int (round towards 0) */
232: st %f2,[%sp+80]
233: ld [%sp+80],%o0 /* %o0 gets int result (via stack temp). */
234: ba 2f
235: nop
236: 1: /* handle negative case. */
237: set 0xc1d00000,%o1 /* %o1 = -2^30 */
238: cmp %o0,%o1 /* if %o0 < -2^30 then range error */
239: bge out_of_range /* not bl because of sign. */
240: nop
241: fdtoi %f0,%f2 /* cvt to int (round towards 0) */
242: st %f2,[%sp+80]
243: fitod %f2,%f4 /* cvt back to real to check for fraction */
244: fcmpd %f0,%f4 /* same value? */
245: ld [%sp+80],%o0 /* %o0 gets int result (via stack temp). */
246: fbe 2f /* check result of fcmpd */
247: nop
248: dec %o0 /* push one lower */
249: 2: /* cvt result to ML int, and continue */
250: add %o0,%o0,%o0
251: add %o0,1,%i0
252: CONTINUE
253:
254: out_of_range: /* out of range, so raise Float("floor"). */
255: set 1f,%i0
256: ba _raise_real
257: String(1, 5, "floor\0\0\0")
258:
259:
260: /* logb : real -> int
261: * Extract and unbias the exponent, return 0 for a zero exponent.
262: * The IEEE bias is 1023.
263: */
264: Closure(_logb_v)
265: ld [%i0],%o0 /* extract exponent. */
266: srl %o0,20,%o0
267: andcc %o0,0x7ff,%o0 /* if (exp == 0) */
268: beq 1f
269: nop
270: sll %o0,1,%o0 /* else unbias and cvt to ML int. */
271: sub %o0,2045,%i0 /* 2(n-1023)+1 == 2n-2045. */
272: 1: CONTINUE
273: 2: ba 1b
274: set 1,%i0 /* return ML zero (delay slot) */
275:
276:
277: /* scalb : (real * int) -> real
278: * Scale the first argument by 2 raised to the second argument. Raise
279: * Float("underflow") or Float("overflow") as appropriate.
280: */
281: Closure(_scalb_v)
282: taddcctv %g6,%g4,%g0 /* check the heap limit. */
283: ld [%i0+4],%o0 /* %o0 gets scale (second arg) */
284: sra %o0,1,%o0 /* cvt scale to sparc int */
285: ld [%i0],%i0 /* %i0 gets real (first arg) */
286: ld [%i0],%o1 /* %o1 gets high word of real value. */
287: set 0x7ff00000,%o3 /* %o3 gets exponent mask. */
288: andcc %o1,%o3,%o4 /* extract exponent into %o4. */
289: beq 1f /* if 0 then return same */
290: nop
291: srl %o4,20,%o4 /* cvt exp to int (delay slot). */
292: addcc %o4,%o0,%o0 /* %o0 = exp + scale */
293: ble under /* if new exp <= 0 then underflow */
294: nop
295: cmp %o0,2047 /* if new exp >= 2047 then overflow */
296: bge over
297: nop
298: andn %o1,%o3,%o1 /* mask out old exponent. */
299: sll %o0,20,%o0 /* shift new exp to exponent position. */
300: or %o1,%o0,%o1 /* set new exponent. */
301: ld [%i0+4],%o0 /* %o0 gets low word of real value. */
302: st %o1,[%g6] /* allocate the new real value */
303: st %o0,[%g6+4]
304: set mak_desc(8,tag_string),%o0
305: st %o0,[%g6-4]
306: mov %g6,%i0 /* set result. */
307: inc 12,%g6 /* storeptr += 3 */
308: 1: CONTINUE
309:
310: over: /* handle overflow */
311: set 1f,%i0
312: ba _raise_real
313: nop
314: String(1, 8, "overflow")
315: under: /* handle underflow */
316: set 1f,%i0
317: ba _raise_real
318: nop
319: String(1, 9, "underflow\0\0\0")
320:
321:
322: /* raise_real:
323: * Raise the exception Float with the string in %i0.
324: */
325: _raise_real:
326: taddcctv %g6,%g4,%g0 /* check the heap limit. */
327: set _real_e,%o0 /* allocate the exception. */
328: st %i0,[%g6]
329: st %o0,[%g6+4]
330: set mak_desc(2,tag_record),%o0
331: st %o0,[%g6-4]
332: mov %g6,%i0
333: inc 12,%g6 /* freeptr += 3 */
334: RAISE
335:
336:
337: .global _endprim
338: _endprim:
339:
340:
341: /* syscall : (int * string list * int) -> int
342: * Note: it is assumed that there are no more than five arguments, plus the code.
343: */
344: Closure(_syscall_v)
345: #define TMPSIZE 20 /* space to save upto five args */
346: #define TMPBASE WINDOWSIZE /* offset of start of temp save area. */
347: mov %i0,%o0
348: save %sp,-SA(WINDOWSIZE+TMPSIZE),%sp
349: SAVE_ML_REGS
350: ld [%i0+8],%o0 /* %o0 = argcount */
351: sra %o0,1,%o0 /* cvt to sparc int */
352: add %sp,TMPBASE,%o1 /* %o1 = base address of temp area */
353: ld [%i0+4],%o2 /* %o2 = arglist */
354: 1: /* loop: process arg list, saving in temp area */
355: deccc 1,%o0 /* if --argcount < 0 then done */
356: bl 3f
357: nop
358: ld [%o2],%o3 /* fetch next arg */
359: btst 1,%o3 /* is the arg an unboxed value? */
360: be 2f
361: ld [%o2+4],%o2 /* advance arg list pointer (delay slot) */
362: sra %o3,1,%o3 /* cvt unboxed arg to sparc int */
363: 2: st %o3,[%o1] /* store arg in temp area */
364: ba 1b
365: inc 4,%o1 /* tempptr++ (delay slot) */
366: /* end of loop */
367: 3: /* load the args into the output registers, we */
368: /* use all six registers even though there may */
369: /* be fewer arguments. */
370: ld [%i0],%o0 /* %o0 = system call number */
371: sra %o0,1,%o0 /* cvt to sparc int */
372: ld [%sp+TMPBASE],%o1
373: ld [%sp+TMPBASE+4],%o2
374: ld [%sp+TMPBASE+8],%o3
375: ld [%sp+TMPBASE+12],%o4
376: ld [%sp+TMPBASE+16],%o5
377: clr %g1 /* 0 is code for syscall. */
378: ta %g0
379: blu 5f
380: nop
381: sll %o0,1,%o0 /* cvt result to ML int */
382: inc 1,%o0
383: 4: RESTORE_ML_REGS
384: restore %o0,0,%i0 /* restore register window, returning result */
385: CONTINUE
386: 5: /* an error, so return -1 */
387: STORE (%o0, _errno)
388: ba 4b
389: set -1,%o0 /* (delay slot) */
390:
391: /* callc : ('b * 'a) -> int
392: * Call a C function with one argument.
393: */
394: Closure(_callc_v)
395: mov %i0,%o0
396: save %sp,-SA(WINDOWSIZE),%sp
397: SAVE_ML_REGS
398: ld [%i0],%o1 /* %o1 = address of the C function */
399: jmpl %o1,%o7 /* call the C function */
400: ld [%i0+4],%o0 /* %o0 = arg (delay slot) */
401: RESTORE_ML_REGS
402: restore %o0,0,%i0 /* restore ML register window, returning result */
403: FETCH (_cause, %o4)
404: tst %o4 /* if (cause != 0) */
405: bne _saveregs /* then return up to the C environment. */
406: nop /* (delay slot) */
407: CONTINUE
408:
409:
410: /* Space to save pointer and non-pointer registers; this is allocated here to
411: * insure quad-word alignment.
412: */
413: .seg "data"
414: .global _saved_ptrs,_saved_nonptrs
415: .align 8
416: _saved_ptrs:
417: .skip (32*4)
418: .align 8
419: _saved_nonptrs:
420: .skip (32*4)
421: .seg "text"
422:
423:
424: /* handle_c, return_c, saveregs and restoreregs:
425: *
426: * handle_c is the root exception handler continuation.
427: * return_c is the root return continuation.
428: * saveregs saves the current ML registers and returns to C code.
429: * restoreregs restores the ML registers and jumps to the ML code address
430: * in save_pc.
431: */
432: .global _handle_c, _return_c, _saveregs, _restoreregs
433: .global _inML, _fault_pending, _fault_code
434:
435: Closure(_handle_c)
436: ba set_cause
437: set CAUSE_EXN,%o4
438:
439: Closure(_return_c)
440: set CAUSE_RET,%o4
441: set_cause:
442: STORE (%o4, _cause)
443: _saveregs:
444: add %i3,-4096,%i3 /* adjust the base code ptr (sub 4096) */
445: set _saved_ptrs,%o3
446: std %i0,[%o3] /* save %i0, %i1 */
447: std %i2,[%o3+8] /* save %i2, %i3 */
448: std %i4,[%o3+16] /* save %i4, %i5 */
449: std %l0,[%o3+24] /* save %l0, %l1 */
450: std %l2,[%o3+32] /* save %l2, %l3 */
451: std %l4,[%o3+40] /* save %l4, %l5 */
452: std %l6,[%o3+48] /* save %l6, %l7 */
453: std %g2,[%o3+56] /* save %g2, %g3 */
454: st %g1,[%o3+64] /* save %g1 */
455: set _saved_nonptrs,%o3
456: std %o0,[%o3] /* save %o0, %o1 as non-pointers */
457: STORE (%g0, _inML) /* note that we are leaving ML code */
458: STORE (%g7, _saved_exnptr)
459: dec 4,%g6 /* adjust store pointer */
460: STORE (%g6, _saved_dataptr)
461: STORE (%g5, _saved_storeptr)
462: #ifdef OLD
463: set 0x7ffffffc,%o0 /* adjust limit ptr */
464: sub %o0,%g4,%g4
465: STORE (%g4, _saved_limit)
466: #endif
467: ldd [%sp+64],%g6 /* restore C registers %g6 & %g7. */
468: ret
469: restore /* restore C register window (delay slot) */
470:
471: _restoreregs:
472: save %sp,-SA(ML_FRAMESIZE),%sp
473: STORE (%sp, _bottom) /* record the base of the ML frame */
474: std %g6,[%sp+64] /* save C registers %g6 & %g7 */
475: set _ml_mul,%o0 /* set pointer to ml_mul */
476: st %o0,[%sp+72]
477: set _ml_div,%o0 /* set pointer to ml_div */
478: st %o0,[%sp+76]
479: FETCH (_saved_exnptr, %g7)
480: FETCH (_saved_dataptr, %g6)
481: inc 4,%g6 /* adjust store pointer */
482: FETCH (_saved_storeptr, %g5)
483: FETCH (_saved_limit, %g4)
484: set 0x7ffffffc,%o0 /* adjust limit ptr */
485: sub %o0,%g4,%g4
486: set 1,%o0 /* note that we are entering ML code */
487: STORE (%o0, _inML)
488: set _saved_ptrs,%o3
489: ldd [%o3],%i0 /* restore %i0, %i1 */
490: ldd [%o3+8],%i2 /* restore %i2, %i3 */
491: ldd [%o3+16],%i4 /* restore %i4, %i5 */
492: ldd [%o3+24],%l0 /* restore %l0, %l1 */
493: ldd [%o3+32],%l2 /* restore %l2, %l3 */
494: ldd [%o3+40],%l4 /* restore %l4, %l5 */
495: ldd [%o3+48],%l6 /* restore %l6, %l7 */
496: ldd [%o3+56],%g2 /* restore %g2, %g3 */
497: ld [%o3+64],%g1 /* restore %g1 */
498: set _saved_nonptrs,%o3
499: ldd [%o3],%o0 /* restore %o0, %o1 as non-pointers */
500: FETCH (_fault_pending, %o4) /* check for a pending fault */
501: tst %o4
502: bne _fault
503: nop
504: sub %i3,-4096,%i3 /* adjust the base code ptr (add 4096) */
505: FETCH (_saved_pc, %o4) /* fetch the ML code address. */
506: jmp %o4 /* invoke the ML code */
507: nop
508:
509: _fault: /* there is a pending fault */
510: clr %o4 /* clear the pending fault flag */
511: STORE (%o4, _fault_pending)
512: FETCH (_fault_code, %i0) /* get the fault exception packet. */
513: RAISE /* raise the exception */
514:
515: /** Integer multiplication and division routines **
516: *
517: * The arguments are %o2, %o3 and the result is in %o2.
518: * Note: this code assumes that .mul and .div don't trash any global or input
519: * registers.
520: */
521: .global .mul, .div
522:
523: /* ml_mul:
524: * multiply %o2 by %o3, returning the result in %o2
525: * Note: this code assumes that .mul doesn't trash any global or input
526: * registers.
527: */
528: _ml_mul:
529: save %sp,-SA(WINDOWSIZE),%sp
530: mov %i2,%o0
531: call .mul
532: mov %i3,%o1 /* (delay slot) */
533: bnz 1f /* if z is clear, then overflow */
534: restore %o0,0,%o2 /* result in %o2 (delay slot) */
535: retl
536: nop
537: 1: /* handle overflow. */
538: set _overflow_e,%i0
539: RAISE
540:
541: /* ml_div:
542: * divide %o2 by %o3, returning the result in %o2.
543: * Note: .div uses %g1, %g2 and %g3, so we must save them. We do this using the
544: * locals of the new window, since .div is a leaf routine.
545: */
546: _ml_div:
547: save %sp,-SA(WINDOWSIZE),%sp
548: addcc %i3,%g0,%o1 /* %o1 is divisor (and check for zero) */
549: bz 1f
550: /* save %g1, %g2 and %g3 (using new window) */
551: mov %g1,%l1 /* (delay slot) */
552: mov %g2,%l2
553: mov %g3,%l3
554: call .div
555: mov %i2,%o0 /* (delay slot) */
556: /* restore %g1, %g2 and %g3 */
557: mov %l3,%g3
558: mov %l2,%g2
559: mov %l1,%g1
560: ret
561: restore %o0,0,%o2 /* result in %o2 (delay slot) */
562: 1: /* handle zero divide */
563: restore /* restore ML window */
564: set _div_e,%i0
565: RAISE
566:
567:
568: /* this bogosity is for export.c */
569: .global _startptr
570: _startptr:
571: .long start
572:
573:
574: #include <sun4/trap.h>
575:
576: /* ml_longjmp:
577: * Restore the ML register window.
578: */
579: .global _ml_longjmp
580: _ml_longjmp:
581: t ST_FLUSH_WINDOWS /* flush all reg windows to the stack */
582: FETCH (_bottom, %fp) /* set %fp to the %sp of the ML frame */
583: sub %fp,SA(WINDOWSIZE),%sp
584: restore /* restore the ML frame, also restoring */
585: /* the local and in registers. */
586: ba _saveregs
587: nop
588:
589:
590: /* _set_fpsr:
591: *
592: * Load the floating-point status register with the given word.
593: */
594: .global _set_fsr
595: _set_fsr:
596: set fsrtmp,%o1
597: st %o0,[%o1]
598: retl
599: ld [%o1],%fsr /* (delay slot) */
600: .seg "data"
601: fsrtmp: .word 0
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.