/* $Header:frame.c 12.0$ */
/* $ACIS:frame.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/dbx/ibmrt/RCS/frame.c,v $ */

#ifndef lint
static char *rcsid = "$Header:frame.c 12.0$";
#endif


/* Copyright (c) 1982 Regents of the University of California */

/*
 * Machine-dependent runtime routines.
 */

#include "defs.h"
#include "runtime.h"
#include "frame.h"
#include "process.h"
#include "machine.h"
#include "events.h"
#include "mappings.h"
#include "symbols.h"
#include "tree.h"
#include "eval.h"
#include "operators.h"
#include "object.h"
#include <sys/param.h>
#include <signal.h>

#ifndef public

#include "machine.h"

#define NSAVEREG NREG+NFLREG 

typedef enum {
    Unknown, AIX_C, Assembler, startup, Signal, As2, X6, normal, Ada,
    nFrameTypes
} FrameType;

struct Frame {
    int condition_handler;
    Address ap;			/* argument pointer */
    Address fp;			/* frame pointer */
    Address pc;			/* program counter */
    Word reg[NSAVEREG];		/* not necessarily there */

    /*
     * Extra stuff for RT PC.  Compilers generate a table
     * at the end of each routine with information
     * necessary for examining stack frames.
     * Below is the information we extract from the table.
     */
    boolean prolog;		/* pc is in prolog */
    FrameType rtype;
    int foffset;		/* offset from frame pointer to top of stack */
    short firstreg;		/* first general register saved */
    short firstfreg;		/* first floating register saved */
    short regoff;		/* address of first saved general register */
    short fregoff;		/* address of first saved floating register */
    short fptr;			/* register used as frame ptr */
    short nparams;		/* number of words of parameters */
    short paramloc[4];		/* param locs: 0 => stack, reg otherwise */
};

#define savepc(frp) ((frp)->pc)
#define frameeq(f1, f2) ((f1)->pc == (f2)->pc)

#endif

#define inSignalHandler(addr) ((addr) > 0x10000000 && (addr) < 0x20000000)

public Frame curframe = nil;
public struct Frame curframerec;

typedef enum { okay, stackend, complain } UnwindType;

static UnwindType unwind[nFrameTypes] = {
    complain, complain, okay, stackend, complain, okay, complain, okay, okay
};

/*
 * Trace table structures.
 */

typedef struct {
    char flag1;		/* always 0xDF */
    char rtype;		/* routine type (massive overkill) */
    char flag2;		/* always 0xDF */
    u_int gprs : 4;	/* first GPR saved */
    u_int got_w : 1;	/* true if w_byte extension present */
    u_int got_x : 1;	/* true if x_byte extension present */
    u_int got_y : 1;	/* true if y_byte extension present */
    u_int got_z : 1;	/* true if z_byte extension present */
} TraceBase;

typedef struct {	/* present if got_w above is true */
    u_int npars : 4;	/* number of parameter words */
    u_int fp_reg : 4;	/* reg used as frame ptr */
} TraceParams;

typedef struct {	/* present if got_x above is true */
    u_int fprs : 4;	/* first float register saved 0=none */
    u_int p0inreg : 1;	/* true if parameter word 0 left in register */
    u_int p1inreg : 1;	/* true if parameter word 1 left in register */
    u_int p2inreg : 1;	/* true if parameter word 2 left in register */
    u_int p3inreg : 1;	/* true if parameter word 3 left in register */
} TraceFloats;

typedef struct {	/* routine offset information */
    u_int osize : 2;	/* number of bytes in size field -1 */
    u_int fsize : 6;	/* top 6 bits of word in frame */
    char fext[3];	/* upto 3 more bytes of size field */
} TraceOffset;

#ifndef OLDCC
#   define LINKSIZE	20	/* bytes in the black hole in the stack frame*/
#   define REGARGSIZE	16
#   define YFIXUP	0
#else
#   define LINKSIZE	0	/* not used in old calling convention */
#   define REGARGSIZE	0
#   define YFIXUP	16
#endif

#define LastGPRsaved	15	/* last general register to save */
#define LastFPRsaved	5	/* last FP register to save (6 not preserved)*/
#define MaxGPRsave	64	/* bytes in maximum GP register save area */
#define MaxFPRsave	64	/* bytes in maximum FP register save area */

#define MAXSAVE (LINKSIZE + REGARGSIZE + 10*sizeof(Word) + 4*sizeof(double))

/*
 * Move the object address for a procedure or function up the
 * appropriate amount.
 */

public findbeginning (f)
Symbol f;
{
    f->symvalue.funcv.beginaddr += FUNCOFFSET;
}

/*
 * Check to see that a given address is a legitimate code address and
 * generate a message if not.
 */

private boolean ValidText (addr)
Address addr;
{
    if (addr >= objsize) {
	warning("text address 0x%x too high (max = 0x%x)", addr, objsize);
	return false;
    } else if ((addr & 0xf0000000) != 0) {
	warning("bad text address 0x%x", addr);
	return false;
    }
    return true;
}

/*
 * Find the address of the trace table following the procedure
 * at the given address.  Return true if there is one, and read the table
 * information into the given frame.
 */

private boolean findtable (addr, frp)
Address addr;
Frame frp;
{
    register Address a;
    char buf[4];

    for (a = addr; ; a++) {
	if (!ValidText(a)) {
	    break;
	}
	iread(buf, a, sizeof(buf));
	if (buf[0] == 0xdf && buf[2] == 0xdf) {
	    readtable(a, frp);
	    return true;
	}
	if (buf[1] != 0xdf) {
	    ++a;
	    if (buf[2] != 0xdf) {
		++a;
		if (buf[3] != 0xdf) {
		    ++a;
		}
	    }
	}
    }
    return false;
}

private readtable (startaddr, frp)
Address startaddr;
register Frame frp;
{
    Address addr;
    TraceBase tbase;
    register int i;

    for (i = 0; i < 4; i++) {
	frp->paramloc[i] = i + 2;
    }
    addr = startaddr;
    iread(&tbase, addr, 4);
    addr += 4;
    frp->rtype = (FrameType) tbase.rtype;
    frp->firstreg = tbase.gprs ? tbase.gprs : 16;
    readw(tbase.got_w, &addr, frp);
    readx(tbase.got_x, &addr, frp);
    if (tbase.got_y) {
	++addr;
    }
    if (tbase.got_z) {
	++addr;
    }
    readoff(&addr, frp);
    frp->regoff = frp->foffset - (4*(16-frp->firstreg)+LINKSIZE+REGARGSIZE);
    if (frp->firstfreg <= LastFPRsaved) {
	frp->fregoff = frp->regoff - 8 * (LastFPRsaved + 1 - frp->firstfreg);
    } else {
	frp->fregoff = frp->regoff;
    }
}

private readw (optw, addr, frp)
boolean optw;
Address *addr;
Frame frp;
{
    TraceParams tp;

    if (optw) {
	iread(&tp, *addr, 1);
	*addr += 1;
	frp->nparams = tp.npars;
	frp->fptr = tp.fp_reg;
    } else {
	frp->nparams = 0;
	frp->fptr = 1;
    }
}

private readx (optx, addr, frp)
boolean optx;
Address *addr;
Frame frp;
{
    TraceFloats tf;
    register unsigned mask, tfmask;
    int i;

    if (optx) {
	iread(&tf, *addr, 1);
	*addr += 1;
	frp->firstfreg = tf.fprs;
	mask = 0x8;
	tfmask = *(char *)&tf;
	for (i = 2; i <= 5; i++) {
	    if ((tfmask & mask) != 0) {
		frp->paramloc[i-2] = i;
	    }
	    mask >>= 1;
	}
    } else {
	frp->firstfreg = 7;
    }
}

private readoff (addr, frp)
register Address *addr;
Frame frp;
{
    TraceOffset toff;
    unsigned char t;
    unsigned int off;
    int i, nbytes;

    if (frp->rtype == Assembler || frp->rtype == As2) {
	frp->foffset = 0;
    } else {
	iread(&toff, *addr, 1);
	*addr += 1;
	nbytes = toff.osize;
	off = toff.fsize;
	for (i = 0; i < nbytes; i++) {
	    off <<= 8;
	    iread(&t, *addr, 1);
	    *addr += 1;
	    off |= t;
	}
	frp->foffset = off << 2;
    }
}

public boolean isprolog (addr)
Address addr;
{
    Symbol f;

    f = whatblock(addr);
    while (isinline(f)) {
	f = container(f);
    }
    return (boolean) (addr <= (codeloc(f) + 8));
}

/*
 * Set a frame to the current activation record.
 */

public getcurframe (frp)
register Frame frp;
{
    register Address a;
    register int i;

    a = reg(PROGCTR);
    if (findtable(a, frp)) {
	frp->prolog = isprolog(a);
	frp->pc = a;
	frp->fp = reg(frp->fptr);
	frp->ap = frp->fp + frp->foffset - REGARGSIZE;
	for (i = 0; i < NSAVEREG; i++) {
	    frp->reg[i] = reg(i);
	}
    }
}

/*
 * Return a pointer to the next activation record up the stack or
 * nil if there is none.  Normally, this routine writes over
 * the given Frame space; therefore it is important to perform
 * the field assignments in the correct order.
 */

public Frame nextframe (frp)
register Frame frp;
{
    register Word *r;
    struct Frame frame;
    Address callpc, off;
    struct sigcontext *sc;
    int i;

    if (unwind[(int)frp->rtype] != okay) {
	if (unwind[(int)frp->rtype] != stackend) {
	    warning("routine type %d not supported", frp->rtype);
	}
	return nil;
    }
    if (frp->prolog) {
	callpc = frp->reg[15];
    } else {
	dread(&callpc, frp->ap - LINKSIZE - 4, sizeof(callpc));
    }
    if (inSignalHandler(callpc)) {
	sc = (struct sigcontext *) (frp->reg[1] + 3*sizeof(Word));
	dread(&callpc, &sc->sc_pc, sizeof(callpc));
	dread(frp->reg, ((Word *) &sc[1]) + 1, sizeof(frp->reg));
	frp->prolog = isprolog(callpc);
    } else if (!ValidText(callpc)) {
	return nil;
    } else if (!frp->prolog) {
	off = frp->fp + frp->regoff;
	for (r = &frp->reg[frp->firstreg]; r < &frp->reg[NSAVEREG]; r++) {
	    dread(r, off, sizeof(*r));
	    off += sizeof(*r);
	}
    } else {
	frp->prolog = false;
    }
    if (!findtable(callpc, &frame)) {
	warning("can't find trace table");
	return nil;
    }
    frp->rtype = frame.rtype;
    frp->foffset = frame.foffset;
    frp->fp = frp->reg[frame.fptr];
    frp->ap = frp->fp + frp->foffset - REGARGSIZE;
    frp->pc = callpc;
    frp->nparams = frame.nparams;
    frp->firstreg = frame.firstreg;
    frp->firstfreg = frame.firstfreg;
    frp->regoff = frame.regoff;
    frp->fregoff = frame.regoff;
    frp->fptr = frame.fptr;
    for (i = 0; i < 4; i++) {
	frp->paramloc[i] = frame.paramloc[i];
    }
    return frp;
}

/*
 * If the given parameter is currently in a register,
 * return the register number; otherwise return -1.
 */

public int preg (param, optfrp)
Symbol param;
Frame optfrp;
{
    register Symbol p;
    register int r, off;
    Frame frp;

    r = -1;
    if (param->block == nil) {
	error("[internal error: nil function containing parameter %s]",
	    symname(param)
	);
    }
    p = param->block->chain;
    if (p == nil) {
	error("[internal error: nil paramlist for function containing %s]",
	    symname(param)
	);
    }
    if (multiword(param->block)) {
	off = 3;
    } else {
	off = 2;
    }
    for (; off <= 5; off++) {
	if (p == param) {
	    if (optfrp == nil) {
		frp = findframe(p->block);
	    } else {
		frp = optfrp;
	    }
	    checkref(frp);
	    if (frp->prolog) {
		r = off;
	    }
	    break;
	}
	p = p->chain;
    }
    return r;
}

/*
 * Return the base address for arguments in the given frame.
 */

public Address args_base (optfrp)
Frame optfrp;
{
    struct Frame frame;
    register Frame frp;

    if (optfrp == nil) {
	getcurframe(&frame);
	frp = &frame;
    } else {
	frp = optfrp;
    }
    return frp->prolog ? (frp->reg[1] - REGARGSIZE) : frp->ap;
}

/*
 * Return the base address for locals in the given frame.
 */

public Address locals_base (optfrp)
Frame optfrp;
{
    struct Frame frame;
    register Frame frp;
    Address base;

    if (optfrp == nil) {
	getcurframe(&frame);
	frp = &frame;
    } else {
	frp = optfrp;
    }
    base = frp->prolog ? (frp->reg[1] - frp->foffset) : frp->fp;
    return base + frp->fregoff;
}

/*
 * Return saved register n from the given frame.
 */

public Word savereg (n, frp)
integer n;
Frame frp;
{
    Word w;

    if (frp == nil) {
	w = reg(n);
    } else {
	switch (n) {
	    case FRP:
		w = frp->fp;
		break;

	    case STKP:
		w = reg(STKP);
		break;

	    case PROGCTR:
		w = frp->pc;
		break;

	    default:
		if (n < 0 or n >= NSAVEREG) {
		    panic("savereg out of range (%d)", n);
		}
		w = frp->reg[n];
		break;
	}
    }
    return w;
}

/*
 * Set the registers according to the given frame pointer.
 */

public getnewregs (addr)
Address addr;
{
    struct Frame frame;

    dread(&frame, addr, sizeof(frame));
    setreg(FRP, frame.fp);
    setreg(PROGCTR, frame.pc);
    pc = frame.pc;
    setcurfunc(whatblock(pc));
}

/*
 * Set saved register n in the given frame.
 */

public setsavereg (n, frp, w)
Integer n;
Frame frp;
Word w;
{
    if (frp == nil) {
	setreg(n,w);
	return;
    }
    switch (n) {
	case FRP:
	    frp->fp = w;
	    break;

	case STKP:
	    setreg(STKP,w);
	    break;

	case PROGCTR:
	    frp->pc = w;
	    break;

	default:
	    assert(n >= 0 and n < NSAVEREG);
	    frp->reg[n] = w;
	    break;
    }
}

/*
 * Return the nth argument of the given frame.
 * If the frame is nil, use the current one.
 */

public Word argn (n, optfrp)
int n;
Frame optfrp;
{
    Word w;
    register Frame frp;
    struct Frame frame;

    if (optfrp == nil) {
	getcurframe(&frame);
	frp = &frame;
    } else {
	frp = optfrp;
    }
    if (n == 0) {
	w = frp->nparams;
    } else if (n >= 1 && n <= 4 && frp->paramloc[n-1] != 0) {
	w = reg(frp->paramloc[n-1]);
    } else {
	dread(&w, args_base(frp) + ((n-1) * sizeof(Word)), sizeof(w));
    }
    return w;
}

/*
 * Push the arguments on the process' stack.  We do this by first
 * evaluating them on the "eval" stack, then copying into the process'
 * space.
 */

public integer pushargs (proc, arglist)
Symbol proc;
Node arglist;
{
    Stack *savesp;
    Word paramreg[4];
    int n, r, argc, args_size;

    savesp = sp;
    if (varIsSet("$unsafecall")) {
	argc = unsafe_evalargs(proc, arglist);
    } else {
	argc = evalargs(proc, arglist);
    }
    args_size = sp - savesp;
    if (args_size > 0) {
	if (args_size > sizeof(paramreg)) {
	    n = sizeof(paramreg) / sizeof(Word);
	    mov(savesp, paramreg, sizeof(paramreg));
	    args_size -= sizeof(paramreg);
	    setreg(STKP, reg(STKP) - args_size);
	    dwrite(savesp + sizeof(paramreg), reg(STKP), args_size);
	} else {
	    n = args_size / sizeof(Word);
	    mov(savesp, paramreg, args_size);
	}
	for (r = 0; r < n; r++) {
	    setreg(r+2, paramreg[r]);
	}
    }
    sp = savesp;
    return argc;
}

/*
 * Pass an expression to a particular parameter.
 *
 * Normally we pass either the address or value, but in some cases
 * (such as C strings) we want to copy the value onto the stack and
 * pass its address.
 *
 * Another special case raised by strings is the possibility that
 * the actual parameter will be larger than the formal, even with
 * appropriate type-checking.  This occurs because we assume during
 * evaluation that strings are null-terminated, whereas some languages,
 * notably Pascal, do not work under that assumption.
 */

public passparam (actual, formal)
Node actual;
Symbol formal;
{
    Address addr;
    Stack *savesp;
    integer actsize, formsize;

    if (formal != nil and isvarparam(formal) and
	(not isopenarray(formal->type))
    ) {
	addr = lval(actual->value.arg[0]);
	push(Address, addr);
    } else if (passaddr(formal, actual->nodetype)) {
	savesp = sp;
	eval(actual);
	actsize = sp - savesp;
	setreg(STKP,
	    reg(STKP) - ((actsize + sizeof(Word) - 1) & ~(sizeof(Word) - 1))
	);
	dwrite(savesp, reg(STKP), actsize);
	sp = savesp;
	push(Address, reg(STKP));
	if (formal != nil and isopenarray(formal->type)) {
	    push(integer, actsize div size(formal->type->type));
	}
    } else if (formal != nil) {
	formsize = size(formal);
	savesp = sp;
	eval(actual);
	actsize = sp - savesp;
	if (actsize > formsize) {
	    sp -= (actsize - formsize);
	}
    } else {
	eval(actual);
    }
}

/*
 * Push the value associated with the current function.
 */

public pushretval (len, isindirect)
integer len;
boolean isindirect;
{
    Word r0;

    r0 = reg(2);
    if (isindirect) {
	rpush((Address) r0, len);
    } else {
	switch (len) {
	    case sizeof(char):
		push(char, r0);
		break;

	    case sizeof(short):
		push(short, r0);
		break;

	    default:
		if (len == sizeof(Word)) {
		    push(Word, r0);
		} else if (len == 2*sizeof(Word)) {
		    push(Word, r0);
		    push(Word, reg(3));
		} else {
		    error("[internal error: bad size %d in pushretval]", len);
		}
		break;
	}
    }
}

/*
 * Flush the debuggee's standard output.
 *
 * This is VERY dependent on the use of stdio.
 */

public flushoutput ()
{
    Symbol p, iob;

    p = lookup(identname("fflush", true));
    while (p != nil and not isblock(p)) {
	p = p->next_sym;
    }
    if (p != nil) {
	iob = lookup(identname("_iob", true));
	if (iob != nil) {
	    pushenv();
	    pc = codeloc(p) - FUNCOFFSET;
	    setreg(2, address(iob, nil) + sizeof(*stdout));
	    beginproc(p, 1);
	    stepto(return_addr());
	    popenv();
	}
    }
}

/* 
 * Ibmrt passes function pointers as the address of beginning of
 * the function's data area. The function address is at the
 * first word of the data area. Getfuncaddr will most likely be
 * a no-op for other compilers/machines.
 */
getfuncaddr(a)
Address a;					/* address of foo's data */
{
	Address x;
	dread(&x, a, sizeof(Address));		/* indirect */
	return(x);
}
