xref: /original-bsd/old/dbx/runtime.tahoe.c (revision a4f2d92b)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)runtime.tahoe.c	5.3 (Berkeley) 01/12/88";
9 #endif not lint
10 
11 static char rcsid[] = "$Header: runtime.tahoe.c,v 1.6 88/01/11 21:26:56 donn Exp $";
12 
13 /*
14  * Runtime organization dependent routines, mostly dealing with
15  * activation records.
16  */
17 
18 #include "defs.h"
19 #include "runtime.h"
20 #include "process.h"
21 #include "machine.h"
22 #include "events.h"
23 #include "mappings.h"
24 #include "symbols.h"
25 #include "tree.h"
26 #include "eval.h"
27 #include "operators.h"
28 #include "object.h"
29 
30 #ifndef public
31 #include "machine.h"
32 
33 typedef struct Frame {
34     Address save_pc;		/* program counter */
35     integer mask:16;		/* register save mask */
36     integer removed:16;		/* 4*number of arguments + 4 */
37     Address save_fp;		/* frame pointer */
38 #define NSAVEREG 13
39     Word save_reg[NSAVEREG];	/* not necessarily there */
40 } *Frame;
41 #endif
42 
43 private Frame curframe = nil;
44 private struct Frame curframerec;
45 private Boolean walkingstack = false;
46 
47 #define frameeq(f1, f2) ((f1)->save_fp == (f2)->save_fp)
48 
49 #define inSignalHandler(addr) \
50     (((addr) < 0xc0000000) and ((addr) > 0xc0000000 - 0x400 * UPAGES))
51 
52 typedef struct {
53     Node callnode;
54     Node cmdnode;
55     boolean isfunc;
56 } CallEnv;
57 
58 private CallEnv endproc;
59 
60 /*
61  * Set a frame to the current activation record.
62  */
63 
64 private getcurframe(frp)
65 Frame frp;
66 {
67     register int i;
68     long x;
69 
70     checkref(frp);
71     frp->save_fp = reg(FRP);
72     dread(&x, frp->save_fp-4, 4);
73     frp->mask = x >> 16;
74     frp->removed = x & 0xffff;
75     frp->save_pc = reg(PROGCTR);
76     for (i = 0; i < NSAVEREG; i++) {
77 	frp->save_reg[i] = reg(i);
78     }
79 }
80 
81 /*
82  * Get the saved registers from one frame to another
83  * given mask specifying which registers were actually saved.
84  */
85 
86 #define bis(b, n) ((b & (1 << (n))) != 0)
87 
88 private getsaveregs (newfrp, frp, mask)
89 Frame newfrp, frp;
90 integer mask;
91 {
92     integer i, j;
93 
94     j = 0;
95     for (i = 0; i < NSAVEREG; i++) {
96 	if (bis(mask, i)) {
97 	    newfrp->save_reg[i] = frp->save_reg[j];
98 	    ++j;
99 	}
100     }
101 }
102 
103 /*
104  * Return a pointer to the next activation record up the stack.
105  * Return nil if there is none.
106  * Writes over space pointed to by given argument.
107  */
108 
109 private Frame nextframe(frp)
110 Frame frp;
111 {
112     Frame newfrp;
113     struct Frame frame;
114     long x;
115     Address prev_frame, callpc;
116     static integer ntramp = 0;
117 
118     newfrp = frp;
119     prev_frame = frp->save_fp;
120 
121 /*
122  *  The check for interrupt generated frames is taken from adb with only
123  *  partial understanding.  If you're in "sub" and on a sigxxx "sigsub"
124  *  gets control, then the stack does NOT look like <main, sub, sigsub>.
125  *
126  *  As best I can make out it looks like:
127  *
128  *     <main, (machine check exception block + sub), sysframe, sigsub>.
129  *
130  *  When the signal occurs an exception block and a frame for the routine
131  *  in which it occured are pushed on the user stack.  Then another frame
132  *  is pushed corresponding to a call from the kernel to sigsub.
133  *
134  *  The addr in sub at which the exception occured is not in sub.save_pc
135  *  but in the machine check exception block.  It is at the magic address
136  *  fp + 84.
137  *
138  *  The current approach ignores the sys_frame (what adb reports as sigtramp)
139  *  and takes the pc for sub from the exception block.  This allows the
140  *  "where" command to report <main, sub, sigsub>, which seems reasonable.
141  */
142 
143 nextf:
144     dread(&frame, prev_frame-8, sizeof(struct Frame));
145     if (ntramp == 1) {
146 	dread(&callpc, prev_frame+44, sizeof(callpc));
147     } else {
148 	callpc = frame.save_pc;
149     }
150     if (frame.save_fp == nil or frame.save_pc == (Address) -1) {
151 	newfrp = nil;
152     } else if (inSignalHandler(callpc)) {
153 	ntramp++;
154 	prev_frame = frame.save_fp;
155 	goto nextf;
156     } else {
157         ntramp = 0;
158 	getsaveregs(newfrp, &frame, frame.mask);
159 	dread(&x, frame.save_fp-4, sizeof (x));
160 	newfrp->mask = x >> 16;
161 	newfrp->removed = x & 0xffff;
162 	newfrp->save_fp = frame.save_fp;
163 	newfrp->save_pc = callpc;
164     }
165     return newfrp;
166 }
167 
168 /*
169  * Get the current frame information in the given Frame and store the
170  * associated function in the given value-result parameter.
171  */
172 
173 private getcurfunc (frp, fp)
174 Frame frp;
175 Symbol *fp;
176 {
177     getcurframe(frp);
178     *fp = whatblock(frp->save_pc);
179 }
180 
181 /*
182  * Return the frame associated with the next function up the call stack, or
183  * nil if there is none.  The function is returned in a value-result parameter.
184  * For "inline" functions the statically outer function and same frame
185  * are returned.
186  */
187 
188 public Frame nextfunc (frp, fp)
189 Frame frp;
190 Symbol *fp;
191 {
192     Symbol t;
193     Frame nfrp;
194 
195     t = *fp;
196     checkref(t);
197     if (isinline(t)) {
198 	t = container(t);
199 	nfrp = frp;
200     } else {
201 	nfrp = nextframe(frp);
202 	if (nfrp == nil) {
203 	    t = nil;
204 	} else {
205 	    t = whatblock(nfrp->save_pc);
206 	}
207     }
208     *fp = t;
209     return nfrp;
210 }
211 
212 /*
213  * Return the frame associated with the given function.
214  * If the function is nil, return the most recently activated frame.
215  *
216  * Static allocation for the frame.
217  */
218 
219 public Frame findframe(f)
220 Symbol f;
221 {
222     Frame frp;
223     static struct Frame frame;
224     Symbol p;
225     Boolean done;
226 
227     frp = &frame;
228     getcurframe(frp);
229     if (f != nil) {
230 	if (f == curfunc and curframe != nil) {
231 	    *frp = *curframe;
232 	} else {
233 	    done = false;
234 	    p = whatblock(frp->save_pc);
235 	    do {
236 		if (p == f) {
237 		    done = true;
238 		} else if (p == program) {
239 		    done = true;
240 		    frp = nil;
241 		} else {
242 		    frp = nextfunc(frp, &p);
243 		    if (frp == nil) {
244 			done = true;
245 		    }
246 		}
247 	    } while (not done);
248 	}
249     }
250     return frp;
251 }
252 
253 /*
254  * Set the registers according to the given frame pointer.
255  */
256 
257 public getnewregs (addr)
258 Address addr;
259 {
260     struct Frame frame;
261     integer i, j, mask;
262 
263     dread(&frame, addr-8, sizeof(frame));
264     setreg(FRP, frame.save_fp);
265     setreg(PROGCTR, frame.save_pc);
266     mask = frame.mask;
267     j = 0;
268     for (i = 0; i < NSAVEREG; i++) {
269 	if (bis(mask, i)) {
270 	    setreg(i, frame.save_reg[j]);
271 	    ++j;
272 	}
273     }
274     pc = frame.save_pc;
275     setcurfunc(whatblock(pc));
276 }
277 
278 /*
279  * Find the return address of the current procedure/function.
280  */
281 
282 public Address return_addr()
283 {
284     Frame frp;
285     Address addr;
286     struct Frame frame;
287 
288     frp = &frame;
289     getcurframe(frp);
290     frp = nextframe(frp);
291     if (frp == nil) {
292 	addr = 0;
293     } else {
294 	addr = frp->save_pc;
295     }
296     return addr;
297 }
298 
299 /*
300  * Push the value associated with the current function.
301  */
302 
303 public pushretval(len, isindirect)
304 integer len;
305 boolean isindirect;
306 {
307     Word r0;
308 
309     r0 = reg(0);
310     if (isindirect) {
311 	rpush((Address) r0, len);
312     } else {
313 	switch (len) {
314 	    case sizeof(char):
315 		push(char, r0);
316 		break;
317 
318 	    case sizeof(short):
319 		push(short, r0);
320 		break;
321 
322 	    default:
323 		if (len == sizeof(Word)) {
324 		    push(Word, r0);
325 		} else if (len == 2*sizeof(Word)) {
326 		    push(Word, r0);
327 		    push(Word, reg(1));
328 		} else {
329 		    error("[internal error: bad size %d in pushretval]", len);
330 		}
331 		break;
332 	}
333     }
334 }
335 
336 /*
337  * Return the base address for locals in the given frame.
338  */
339 
340 public Address locals_base(frp)
341 Frame frp;
342 {
343     return (frp == nil ? reg(FRP) : frp->save_fp);
344 }
345 
346 /*
347  * Return the base address for arguments in the given frame.
348  */
349 
350 public Address args_base(frp)
351 Frame frp;
352 {
353     return (frp == nil ? reg(FRP) : frp->save_fp);
354 }
355 
356 /*
357  * Return saved register n from the given frame.
358  */
359 
360 public Word savereg(n, frp)
361 integer n;
362 Frame frp;
363 {
364     Word w;
365 
366     if (frp == nil) {
367 	w = reg(n);
368     } else {
369 	switch (n) {
370 
371 	    case FRP:
372 		w = frp->save_fp;
373 		break;
374 
375 	    case STKP:
376 		w = reg(STKP);
377 		break;
378 
379 	    case PROGCTR:
380 		w = frp->save_pc;
381 		break;
382 
383 	    default:
384 		assert(n >= 0 and n < NSAVEREG);
385 		w = frp->save_reg[n];
386 		break;
387 	}
388     }
389     return w;
390 }
391 
392 /*
393  * Return the nth argument to the current procedure.
394  */
395 
396 public Word argn(n, frp)
397 integer n;
398 Frame frp;
399 {
400     Address argaddr;
401     Word w;
402 
403     argaddr = args_base(frp) + (n * sizeof(Word));
404     dread(&w, argaddr, sizeof(w));
405     return w;
406 }
407 
408 /*
409  * Print a list of currently active blocks starting with most recent.
410  */
411 
412 public wherecmd()
413 {
414     walkstack(false);
415 }
416 
417 /*
418  * Print the variables in the given frame or the current one if nil.
419  */
420 
421 public dump (func)
422 Symbol func;
423 {
424     Symbol f;
425     Frame frp;
426 
427     if (func == nil) {
428 	f = curfunc;
429 	if (curframe != nil) {
430 	    frp = curframe;
431 	} else {
432 	    frp = findframe(f);
433 	}
434     } else {
435 	f = func;
436 	frp = findframe(f);
437     }
438     showaggrs = true;
439     printcallinfo(f, frp);
440     dumpvars(f, frp);
441 }
442 
443 /*
444  * Dump all values.
445  */
446 
447 public dumpall ()
448 {
449     walkstack(true);
450 }
451 
452 /*
453  * Walk the stack of active procedures printing information
454  * about each active procedure.
455  */
456 
457 private walkstack(dumpvariables)
458 Boolean dumpvariables;
459 {
460     Frame frp;
461     boolean save;
462     Symbol f;
463     struct Frame frame;
464 
465     if (notstarted(process) or isfinished(process)) {
466 	error("program is not active");
467     } else {
468 	save = walkingstack;
469 	walkingstack = true;
470 	showaggrs = dumpvariables;
471 	frp = &frame;
472 	getcurfunc(frp, &f);
473 	for (;;) {
474 	    printcallinfo(f, frp);
475 	    if (dumpvariables) {
476 		dumpvars(f, frp);
477 		putchar('\n');
478 	    }
479 	    frp = nextfunc(frp, &f);
480 	    if (frp == nil or f == program) {
481 		break;
482 	    }
483 	}
484 	if (dumpvariables) {
485 	    printf("in \"%s\":\n", symname(program));
486 	    dumpvars(program, nil);
487 	    putchar('\n');
488 	}
489 	walkingstack = save;
490     }
491 }
492 
493 /*
494  * Print out the information about a call, i.e.,
495  * routine name, parameter values, and source location.
496  */
497 
498 private printcallinfo (f, frp)
499 Symbol f;
500 Frame frp;
501 {
502     Lineno line;
503     Address savepc;
504 
505     savepc = frp->save_pc;
506     if (frp->save_fp != reg(FRP)) {
507 	savepc -= 1;
508     }
509     printname(stdout, f);
510     if (not isinline(f)) {
511 	printparams(f, frp);
512     }
513     line = srcline(savepc);
514     if (line != 0) {
515 	printf(", line %d", line);
516 	printf(" in \"%s\"\n", srcfilename(savepc));
517     } else {
518 	printf(" at 0x%x\n", savepc);
519     }
520 }
521 
522 /*
523  * Set the current function to the given symbol.
524  * We must adjust "curframe" so that subsequent operations are
525  * not confused; for simplicity we simply clear it.
526  */
527 
528 public setcurfunc (f)
529 Symbol f;
530 {
531     curfunc = f;
532     curframe = nil;
533 }
534 
535 /*
536  * Return the frame for the current function.
537  * The space for the frame is allocated statically.
538  */
539 
540 public Frame curfuncframe ()
541 {
542     static struct Frame frame;
543     Frame frp;
544 
545     if (curframe == nil) {
546 	frp = findframe(curfunc);
547 	curframe = &curframerec;
548 	*curframe = *frp;
549     } else {
550 	frp = &frame;
551 	*frp = *curframe;
552     }
553     return frp;
554 }
555 
556 /*
557  * Set curfunc to be N up/down the stack from its current value.
558  */
559 
560 public up (n)
561 integer n;
562 {
563     integer i;
564     Symbol f;
565     Frame frp;
566     boolean done;
567 
568     if (not isactive(program)) {
569 	error("program is not active");
570     } else if (curfunc == nil) {
571 	error("no current function");
572     } else {
573 	i = 0;
574 	f = curfunc;
575 	frp = curfuncframe();
576 	done = false;
577 	do {
578 	    if (frp == nil) {
579 		done = true;
580 		error("not that many levels");
581 	    } else if (i >= n) {
582 		done = true;
583 		curfunc = f;
584 		curframe = &curframerec;
585 		*curframe = *frp;
586 		showaggrs = false;
587 		printcallinfo(curfunc, curframe);
588 	    } else if (f == program) {
589 		done = true;
590 		error("not that many levels");
591 	    } else {
592 		frp = nextfunc(frp, &f);
593 	    }
594 	    ++i;
595 	} while (not done);
596     }
597 }
598 
599 public down (n)
600 integer n;
601 {
602     integer i, depth;
603     Frame frp, curfrp;
604     Symbol f;
605     struct Frame frame;
606 
607     if (not isactive(program)) {
608 	error("program is not active");
609     } else if (curfunc == nil) {
610 	error("no current function");
611     } else {
612 	depth = 0;
613 	frp = &frame;
614 	getcurfunc(frp, &f);
615 	if (curframe == nil) {
616 	    curfrp = findframe(curfunc);
617 	    curframe = &curframerec;
618 	    *curframe = *curfrp;
619 	}
620 	while ((f != curfunc or !frameeq(frp, curframe)) and f != nil) {
621 	    frp = nextfunc(frp, &f);
622 	    ++depth;
623 	}
624 	if (f == nil or n > depth) {
625 	    error("not that many levels");
626 	} else {
627 	    depth -= n;
628 	    frp = &frame;
629 	    getcurfunc(frp, &f);
630 	    for (i = 0; i < depth; i++) {
631 		frp = nextfunc(frp, &f);
632 		assert(frp != nil);
633 	    }
634 	    curfunc = f;
635 	    *curframe = *frp;
636 	    showaggrs = false;
637 	    printcallinfo(curfunc, curframe);
638 	}
639     }
640 }
641 
642 /*
643  * Find the entry point of a procedure or function.
644  */
645 
646 public findbeginning (f)
647 Symbol f;
648 {
649     if (isinternal(f)) {
650 	f->symvalue.funcv.beginaddr += 15;
651     } else {
652 	f->symvalue.funcv.beginaddr += FUNCOFFSET;
653     }
654 }
655 
656 /*
657  * Return the address corresponding to the first line in a function.
658  */
659 
660 public Address firstline(f)
661 Symbol f;
662 {
663     Address addr;
664 
665     addr = codeloc(f);
666     while (linelookup(addr) == 0 and addr < objsize) {
667 	++addr;
668     }
669     if (addr == objsize) {
670 	addr = -1;
671     }
672     return addr;
673 }
674 
675 /*
676  * Catcher drops strike three ...
677  */
678 
679 public runtofirst()
680 {
681     Address addr, endaddr;
682 
683     addr = pc;
684     endaddr = objsize + CODESTART;
685     while (linelookup(addr) == 0 and addr < endaddr) {
686 	++addr;
687     }
688     if (addr < endaddr) {
689 	stepto(addr);
690     }
691 }
692 
693 /*
694  * Return the address corresponding to the end of the program.
695  *
696  * We look for the entry to "exit".
697  */
698 
699 public Address lastaddr()
700 {
701     Symbol s;
702 
703     s = lookup(identname("exit", true));
704     if (s == nil) {
705 	panic("can't find exit");
706     }
707     return codeloc(s);
708 }
709 
710 /*
711  * Decide if the given function is currently active.
712  *
713  * We avoid calls to "findframe" during a stack trace for efficiency.
714  * Presumably information evaluated while walking the stack is active.
715  */
716 
717 public Boolean isactive (f)
718 Symbol f;
719 {
720     Boolean b;
721 
722     if (isfinished(process)) {
723 	b = false;
724     } else {
725 	if (walkingstack or f == program or f == nil or
726 	  (ismodule(f) and isactive(container(f)))) {
727 	    b = true;
728 	} else {
729 	    b = (Boolean) (findframe(f) != nil);
730 	}
731     }
732     return b;
733 }
734 
735 /*
736  * Evaluate a call to a procedure.
737  */
738 
739 public callproc(exprnode, isfunc)
740 Node exprnode;
741 boolean isfunc;
742 {
743     Node procnode, arglist;
744     Symbol proc;
745     integer argc;
746 
747     procnode = exprnode->value.arg[0];
748     arglist = exprnode->value.arg[1];
749     if (procnode->op != O_SYM) {
750 	beginerrmsg();
751 	fprintf(stderr, "can't call \"");
752 	prtree(stderr, procnode);
753 	fprintf(stderr, "\"");
754 	enderrmsg();
755     }
756     assert(procnode->op == O_SYM);
757     proc = procnode->value.sym;
758     if (not isblock(proc)) {
759 	error("\"%s\" is not a procedure or function", symname(proc));
760     }
761     endproc.isfunc = isfunc;
762     endproc.callnode = exprnode;
763     endproc.cmdnode = topnode;
764     pushenv();
765     pc = codeloc(proc);
766     argc = pushargs(proc, arglist);
767     setreg(FRP, 1);	/* have to ensure it's non-zero for return_addr() */
768     beginproc(proc, argc);
769     event_once(
770 	build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
771 	buildcmdlist(build(O_PROCRTN, proc))
772     );
773     isstopped = false;
774     if (not bpact()) {
775 	isstopped = true;
776 	cont(0);
777     }
778     /*
779      * bpact() won't return true, it will call printstatus() and go back
780      * to command input if a breakpoint is found.
781      */
782     /* NOTREACHED */
783 }
784 
785 /*
786  * Push the arguments on the process' stack.  We do this by first
787  * evaluating them on the "eval" stack, then copying into the process'
788  * space.
789  */
790 
791 private integer pushargs(proc, arglist)
792 Symbol proc;
793 Node arglist;
794 {
795     Stack *savesp;
796     int argc, args_size;
797 
798     savesp = sp;
799     argc = evalargs(proc, arglist);
800     args_size = sp - savesp;
801     setreg(STKP, reg(STKP) - args_size);
802     dwrite(savesp, reg(STKP), args_size);
803     sp = savesp;
804     return argc;
805 }
806 
807 /*
808  * Check to see if an expression is correct for a given parameter.
809  * If the given parameter is false, don't worry about type inconsistencies.
810  *
811  * Return whether or not it is ok.
812  */
813 
814 private boolean chkparam (actual, formal, chk)
815 Node actual;
816 Symbol formal;
817 boolean chk;
818 {
819     boolean b;
820 
821     b = true;
822     if (chk) {
823 	if (formal == nil) {
824 	    beginerrmsg();
825 	    fprintf(stderr, "too many parameters");
826 	    b = false;
827 	} else if (not compatible(formal->type, actual->nodetype)) {
828 	    beginerrmsg();
829 	    fprintf(stderr, "type mismatch for %s", symname(formal));
830 	    b = false;
831 	}
832     }
833     if (b and formal != nil and
834 	isvarparam(formal) and not isopenarray(formal->type) and
835 	not (
836 	    actual->op == O_RVAL or actual->nodetype == t_addr or
837 	    (
838 		actual->op == O_TYPERENAME and
839 		(
840 		    actual->value.arg[0]->op == O_RVAL or
841 		    actual->value.arg[0]->nodetype == t_addr
842 		)
843 	    )
844 	)
845     ) {
846 	beginerrmsg();
847 	fprintf(stderr, "expected variable, found \"");
848 	prtree(stderr, actual);
849 	fprintf(stderr, "\"");
850 	b = false;
851     }
852     return b;
853 }
854 
855 /*
856  * Pass an expression to a particular parameter.
857  *
858  * Normally we pass either the address or value, but in some cases
859  * (such as C strings) we want to copy the value onto the stack and
860  * pass its address.
861  *
862  * Another special case raised by strings is the possibility that
863  * the actual parameter will be larger than the formal, even with
864  * appropriate type-checking.  This occurs because we assume during
865  * evaluation that strings are null-terminated, whereas some languages,
866  * notably Pascal, do not work under that assumption.
867  */
868 
869 private passparam (actual, formal)
870 Node actual;
871 Symbol formal;
872 {
873     boolean b;
874     Address addr;
875     Stack *savesp;
876     integer actsize, formsize;
877 
878     if (formal != nil and isvarparam(formal) and
879 	(not isopenarray(formal->type))
880     ) {
881 	addr = lval(actual->value.arg[0]);
882 	push(Address, addr);
883     } else if (passaddr(formal, actual->nodetype)) {
884 	savesp = sp;
885 	eval(actual);
886 	actsize = sp - savesp;
887 	setreg(STKP, reg(STKP) - roundup(actsize, sizeof (Word)));
888 	dwrite(savesp, reg(STKP), actsize);
889 	sp = savesp;
890 	push(Address, reg(STKP));
891 	if (formal != nil and isopenarray(formal->type)) {
892 	    push(integer, actsize div size(formal->type->type));
893 	}
894     } else if (formal != nil) {
895 	formsize = size(formal);
896 	savesp = sp;
897 	eval(actual);
898 	actsize = sp - savesp;
899 	if (actsize > formsize) {
900 	    sp -= (actsize - formsize);
901 	}
902     } else {
903 	eval(actual);
904     }
905 }
906 
907 /*
908  * Evaluate an argument list left-to-right.
909  */
910 
911 private integer evalargs(proc, arglist)
912 Symbol proc;
913 Node arglist;
914 {
915     Node p, actual;
916     Symbol formal;
917     Stack *savesp;
918     integer count;
919     boolean chk;
920 
921     savesp = sp;
922     count = 0;
923     formal = proc->chain;
924     chk = (boolean) (not nosource(proc));
925     for (p = arglist; p != nil; p = p->value.arg[1]) {
926 	assert(p->op == O_COMMA);
927 	actual = p->value.arg[0];
928 	if (not chkparam(actual, formal, chk)) {
929 	    fprintf(stderr, " in call to %s", symname(proc));
930 	    sp = savesp;
931 	    enderrmsg();
932 	}
933 	passparam(actual, formal);
934 	if (formal != nil) {
935 	    formal = formal->chain;
936 	}
937 	++count;
938     }
939     if (chk) {
940 	if (formal != nil) {
941 	    sp = savesp;
942 	    error("not enough parameters to %s", symname(proc));
943 	}
944     }
945     return count;
946 }
947 
948 /*
949  * Evaluate an argument list without any type checking.
950  * This is only useful for procedures with a varying number of
951  * arguments that are compiled -g.
952  */
953 
954 private integer unsafe_evalargs (proc, arglist)
955 Symbol proc;
956 Node arglist;
957 {
958     Node p;
959     integer count;
960 
961     count = 0;
962     for (p = arglist; p != nil; p = p->value.arg[1]) {
963 	assert(p->op == O_COMMA);
964 	eval(p->value.arg[0]);
965 	++count;
966     }
967     return count;
968 }
969 
970 public procreturn(f)
971 Symbol f;
972 {
973     integer retvalsize;
974     Node tmp;
975     char *copy;
976 
977     flushoutput();
978     popenv();
979     if (endproc.isfunc) {
980 	retvalsize = size(f->type);
981 	if (retvalsize > sizeof(long)) {
982 	    pushretval(retvalsize, true);
983 	    copy = newarr(char, retvalsize);
984 	    popn(retvalsize, copy);
985 	    tmp = build(O_SCON, copy);
986 	} else {
987 	    tmp = build(O_LCON, (long) (reg(0)));
988 	}
989 	tmp->nodetype = f->type;
990 	tfree(endproc.callnode);
991 	*(endproc.callnode) = *(tmp);
992 	dispose(tmp);
993 	eval(endproc.cmdnode);
994     } else {
995 	putchar('\n');
996 	printname(stdout, f);
997 	printf(" returns successfully\n");
998     }
999     erecover();
1000 }
1001 
1002 /*
1003  * Push the current environment.
1004  */
1005 
1006 private pushenv()
1007 {
1008     push(Address, pc);
1009     push(Lineno, curline);
1010     push(String, cursource);
1011     push(Boolean, isstopped);
1012     push(Symbol, curfunc);
1013     push(Frame, curframe);
1014     push(struct Frame, curframerec);
1015     push(CallEnv, endproc);
1016     push(Word, reg(PROGCTR));
1017     push(Word, reg(STKP));
1018 }
1019 
1020 /*
1021  * Pop back to the real world.
1022  */
1023 
1024 public popenv()
1025 {
1026     String filename;
1027 
1028     setreg(STKP, pop(Word));
1029     setreg(PROGCTR, pop(Word));
1030     endproc = pop(CallEnv);
1031     curframerec = pop(struct Frame);
1032     curframe = pop(Frame);
1033     curfunc = pop(Symbol);
1034     isstopped = pop(Boolean);
1035     filename = pop(String);
1036     curline = pop(Lineno);
1037     pc = pop(Address);
1038     setsource(filename);
1039 }
1040 
1041 /*
1042  * Flush the debuggee's standard output.
1043  *
1044  * This is VERY dependent on the use of stdio.
1045  */
1046 
1047 public flushoutput()
1048 {
1049     Symbol p, iob;
1050     Stack *savesp;
1051 
1052     p = lookup(identname("fflush", true));
1053     while (p != nil and not isblock(p)) {
1054 	p = p->next_sym;
1055     }
1056     if (p != nil) {
1057 	iob = lookup(identname("_iob", true));
1058 	if (iob != nil) {
1059 	    pushenv();
1060 	    pc = codeloc(p);
1061 	    savesp = sp;
1062 	    push(long, address(iob, nil) + sizeof(struct _iobuf));
1063 	    setreg(STKP, reg(STKP) - sizeof(long));
1064 	    dwrite(savesp, reg(STKP), sizeof(long));
1065 	    sp = savesp;
1066 	    beginproc(p, 1);
1067 	    stepto(return_addr());
1068 	    popenv();
1069 	}
1070     }
1071 }
1072