/* $Id: saol_rates.c,v 1.7 1997/11/05 20:10:43 eds Exp $ */
/* $Log: saol_rates.c,v $
 * Revision 1.7  1997/11/05  20:10:43  eds
 * Added tablemaps.
 *
 * Revision 1.6  1997/11/05  15:02:08  eds
 * xopcodes bump up to enclosing guard rate.
 * Added vector parameters/return values/operators.
 *
 * Revision 1.5  1997/10/03  15:11:57  eds
 * Added sfsynth statement.
 *
 * Revision 1.4  1997/09/30  15:39:57  eds
 * Added opcode name to 'too slow for guard' error message.
 *
 * Revision 1.3  1997/09/17  16:20:40  eds
 * New rule: Can't embed slow opcode calls in fast if/while blocks.
 *
 * Revision 1.2  1997/09/17  14:11:09  eds
 * Added spatialize statement.
 * */
/*********************************************************************

This software module was originally developed by

Eric D. Scheirer (MIT Media Laboratory)

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard.  ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation.

This software module is hereby released into the public domain.

***********************************************************************/

#include <string.h>
#include <stdio.h>
#include <math.h>
#include "saol.h"
#include "saol_co_imp.h"
#include "y.tab.h"
#include <malloc.h>

#define ratestr(r) (r == ASIG) ? "asig" : ((r == KSIG) ? "ksig" : ((r == IVAR) ? "ivar" : "xsig"))

#ifndef MAX
#define MAX(x,y) ((x > y) ? x : y)
#endif

void max_rate_check_exprlist(sa_decoder *sa, symtable *t, exprlist *el, long maxrate);
long block_rate(sa_decoder *sa, symtable *t, block *b, long guardrate, long oprate,
    long xsigminrate);
long statement_rate(sa_decoder *sa, symtable *t, statement *st, long maxrate,
            long xsigminrate, long guardrate);
long expr_rate(sa_decoder *,symtable *t, expr *p, long guardrate, long xsigminrate);
long opcode_rate(sa_decoder *sa, symtable *t, exprlist *el, opcode_decl *op, int line,
		 long guardrate, long xsigminrate);
void check_table_def(symtable *t,tabledef *td);

void rate_checking(sa_decoder *sa) {
  /* make sure all the rates in the orchestra are correct */

  vardecl_list *vdl;
  opcode_list *op;
  instr_list *il;
  send_list *sl;
  formalparam_list *fpl;
  char s[1000];
 
  /* first, assign tentative rates to each opcode */
  for (op = sa->all->op; op; op=op->next) {
    op->o->rate = 0;
    op->o->minxsigrate = 0;
    switch (op->o->type) {
    case AOPCODE:
      op->o->rate = ASIG;
      op->o->minxsigrate = ASIG;
      break;
    case KOPCODE:
      op->o->rate = KSIG;
      op->o->minxsigrate = KSIG;
      break;
    case IOPCODE:
      op->o->rate = IVAR;
      op->o->minxsigrate = IVAR;
      break;
    case SPECIALOP:
      op->o->rate = SPECIALOP;
      op->o->minxsigrate = SPECIALOP;
    case OPCODE:
      fpl = op->o->params;
      if (!fpl || !fpl->fp) { /* no formal parameters */
        sprintf(s,"Cannot declare 'opcode %s' with no formal parameters.",op->o->name);
        parse_error_line(s,1);
        }
      for (fpl=op->o->params;fpl;fpl=fpl->next) {
  
        /* figure out minimum rate for all xsigs in this opcode */
        /* (tables are like ivars) */
        
        if (fpl->fp && fpl->fp->type != XSIG &&
          fpl->fp->type > op->o->minxsigrate) {
          if (fpl->fp->type == TABLE) {
            if (IVAR > op->o->minxsigrate)
              op->o->minxsigrate = IVAR;
            }
          else op->o->minxsigrate = fpl->fp->type;
        }
        /* and minimum rate for opcode overall */
        if (fpl->fp && fpl->fp->type > op->o->rate)
          if (fpl->fp->type == TABLE && IVAR > op->o->rate)
            op->o->rate = IVAR;
          else op->o->rate = fpl->fp->type;  
        }
      break;

    default:
      printf("Bad opcode type (%d) in rate_checking!",op->o->type);
    }
  }

  /* any global table initializers */
  for (vdl = sa->all->g->gvars; vdl; vdl=vdl->next)
    if (vdl->v->type == TABLE)
      check_table_def(sa->all->g->sym,vdl->v->t);

  /* parameters to sends */
  for (sl = sa->all->g->sends; sl; sl = sl->next)
    max_rate_check_exprlist(sa,sa->all->g->sym,sl->s->pfields,IVAR);

  /* parameters to input modifier */
  if (sa->all->g->inmod)
    max_rate_check_exprlist(sa,sa->all->g->sym,sa->all->g->inmod->params,IVAR);

  /* each instrument */

  for (il = sa->all->il; il; il=il->next) {

    /* table initializers */
    for (vdl = il->i->localvars; vdl; vdl=vdl->next)
      if (vdl->v && vdl->v->t)
	check_table_def(il->i->sym,vdl->v->t);

    /* code */
   block_rate(sa,il->i->sym, il->i->code, 0, ASIG, ASIG);

  }

  /* each opcode */
  for (op = sa->all->op; op; op=op->next) {

    /* check parameters */
    for (fpl = op->o->params; fpl && fpl->fp; fpl=fpl->next) 
      if (fpl->fp->type > op->o->rate && fpl->fp->type != TABLE &&
    op->o->rate != ASIG && op->o->type != OPCODE) {
  if (fpl->fp->type == XSIG)
    sprintf(s,"Formal parameter '%s' might be faster than opcode.",
      fpl->fp->n->name);
  else
    sprintf(s,"Formal parameter '%s' is declared faster than opcode.",
      fpl->fp->n->name);
  parse_error_line(s,fpl->fp->lineno);
      }
      
    /* local variables & table initializers */
    for (vdl = op->o->localvars; vdl && vdl->v; vdl=vdl->next) {
      if (vdl->v->t)
  check_table_def(op->o->sym,vdl->v->t);
      else {
	if (vdl->v->type != XSIG && vdl->v->type != TABLE && vdl->v->type != OPARRAY){
    if (vdl->v->type > op->o->rate)
      parse_error_line("Variable declared faster than opcode.",
           vdl->v->nl->lineno);
    
    if (op->o->rate == XSIG && vdl->v->type > op->o->minxsigrate)
      parse_error_line("Variable might be faster than opcode.",
           vdl->v->nl->lineno);
  }
      }

    }

    /* code */
    block_rate(sa,op->o->sym, op->o->code, 0, op->o->rate, op->o->minxsigrate);
  }
}

long block_rate(sa_decoder *sa, symtable *t, block *b, long guardrate, long oprate,
    long xsigminrate) {
  long r,fastest =0;
  
  for (;b;b=b->next)
    if (b->st) {
      r = statement_rate(sa, t,b->st,oprate,xsigminrate,guardrate);
      if (r > fastest) fastest = r;
      
      b->st->rate = r;
      if (b->st->rate > oprate)
        if (b->st->rate != XSIG &&
          (b->st->rate != SPECIALOP || oprate == IVAR))
          parse_error_line("Statement too fast for opcode.",b->st->lineno);
        else if (oprate != ASIG)
          parse_error_line("Statement might be too fast for opcode.",
          b->st->lineno);
        
      if (b->st->rate < guardrate)
        if (guardrate != XSIG)
          parse_error_line("Statement too slow for enclosing guard.",
          b->st->lineno);
        else if (b->st->rate < xsigminrate)
          parse_error_line("Statement might be too slow for enclosing guard.",
          b->st->lineno);
        
      if (b->st->rate == XSIG && guardrate != XSIG && xsigminrate < guardrate)
        parse_error_line("Statement might be too slow for enclosing guard (2).",
        b->st->lineno);

    }
    return(fastest);
}

long statement_rate(sa_decoder *sa, symtable *t, statement *st, long maxrate,
            long xsigminrate, long guardrate) {
  long rate1, rate2, rate3;
  exprlist *el;
  
  
  if (!st) return 0;

  switch (st->type) {
  case EQ:
    if (st->lvalue) {
      rate1 = expr_rate(sa,t,st->lvalue,xsigminrate,guardrate);
      rate2 = expr_rate(sa,t,st->expr,xsigminrate,guardrate);

    /* check assignment rates */
      if (rate1 < rate2 && rate2 != XSIG && rate2 != SPECIALOP) 
	parse_error_line("Assigned expression is faster than lvalue",
			 st->lineno);
      if (rate2 == XSIG && rate1 < ASIG)
	parse_error_line("Assigned expression might be faster than lvalue",
			 st->lineno);
      if (xsigminrate && rate1 == XSIG && rate2 != XSIG && xsigminrate < rate2)
	parse_error_line("Assigned expression might be faster than lvalue",
			 st->lineno);
      
      /* assignment is okay; overall rate is lvalue rate */
      if (rate2 != SPECIALOP)
	return(rate1);
      else
	return(SPECIALOP);
    }
    else {
      rate1 = expr_rate(sa,t,st->expr,xsigminrate,guardrate);
      return(rate1);
    }
  case IF:
  case WHILE:
    rate1 = expr_rate(sa,t,st->expr,xsigminrate,guardrate);

    rate1 = block_rate(sa,t, st->b, rate1, maxrate, xsigminrate);
    
    return(rate1);
  case ELSE:
    rate1 = expr_rate(sa,t,st->expr,xsigminrate,guardrate);

    rate2 = block_rate(sa,t, st->b, rate1, maxrate, xsigminrate);
    rate3 = block_rate(sa,t, st->elseb, rate1, maxrate, xsigminrate);
  
    return(MAX(rate2,rate3));
  case SPATIALIZE:
    if (expr_rate(sa,t,st->params->p,xsigminrate,guardrate) != ASIG)
      parse_error_line("Spatialize expression must be a-rate",st->lineno);
    if (expr_rate(sa,t,st->params->next->p,xsigminrate,guardrate) == ASIG)
      parse_error_line("Spatialize azimuth must be k-rate",st->lineno);
    if (expr_rate(sa,t,st->params->next->next->p,xsigminrate,guardrate) == ASIG)
      parse_error_line("Spatialize elevation must be k-rate",st->lineno);
    if (expr_rate(sa,t,st->params->next->next->next->p,xsigminrate,guardrate) == ASIG)
      parse_error_line("Spatialize distance must be a-rate",st->lineno);
    return(ASIG);
  case SFSYNTH:
    rate1 = 0;
    for (el = st->params;el;el=el->next) {
      if ((rate2 = expr_rate(sa,t,el->p,xsigminrate,guardrate)) == ASIG)
	parse_error_line("Sfsynth expression cannot be a-rate.",st->lineno);
      if (rate2 > rate1) rate1 = rate2;
    }
    
    for (el = st->sfextra;el;el=el->next) {
      if ((rate2 = expr_rate(sa,t,el->p,xsigminrate,guardrate)) == ASIG)
	parse_error_line("Sfsynth expression cannot be a-rate.",st->lineno);
      if (rate2 > rate1) rate1 = rate2;
    }
    
    return(rate1);
  case OUTPUT:
  case OUTBUS:
    for (el = st->params;el;el=el->next)
      if (expr_rate(sa,t,el->p,xsigminrate,guardrate) != ASIG)
        parse_error_line("Output expression must be a-rate.",st->lineno);
    return(ASIG);
  case RETURN:
    expr_rate(sa,t,st->expr,xsigminrate,guardrate);
    return(maxrate);
  case TURNOFF:
    return(KSIG);
  case EXTEND:
    rate1 = expr_rate(sa,t,st->expr,xsigminrate,guardrate);
    if (rate1 == ASIG || rate1 == XSIG)
      parse_error_line("Extend expression must be k-rate or slower.",
           st->lineno);
    return(KSIG);
  case INSTR:
    rate1 = KSIG;
    for (el = st->params;el;el=el->next) {
      rate2 = expr_rate(sa,t,el->p,xsigminrate,guardrate);
      if (rate2 > rate1) rate1 = rate2;
    }
    if (rate1 > KSIG)
      parse_error_line("Instr parameters must be k-rate or slower.",
           st->lineno);
    return(rate1);
  default:
    interror("bad statement type in statement_rate");
  }
  return 0;
}
  
long expr_rate(sa_decoder *sa, symtable *t, expr *p,long xsigminrate, long guardrate) {
  symbol *sym;
  char s[1000];
  long r1, r2;
  
  if (!p) return 0;
  
  switch(p->op) {
  case IDENT:
    sym = get_sym_decl(t,p->d->iname);
    if (!sym || sym->type == TABLE) /* if !sym it's a dynamic wavetable */
      return(p->rate = IVAR);
    else return(p->rate = sym->type);
  case NUMBER:
  case STRCONST:
    return(p->rate = IVAR);
  case ARRAYREF:
    r1 = expr_rate(sa,t,p->left,xsigminrate,guardrate);
    sym = get_sym_decl(t,p->d->iname);
    if (sym->type == TABLEMAP)
      return(p->rate = r1);
    else return(p->rate = MAX(r1,sym->type));
  case OPCALL:
    sym = get_opcode_decl(sa->all->g->sym,p->d->iname);
    p->defn = sym;
    p->rate = opcode_rate(sa,t,p->params,(opcode_decl *)sym->defn,p->lineno,
			  xsigminrate,guardrate);
	
    if (p->rate < guardrate)
      if (guardrate != XSIG) {
	sprintf(s,"Opcode '%s' too slow for enclosing guard.",p->d->iname);
	parse_error_line(s,
			 p->lineno);
      }
      else if (p->rate < xsigminrate)
	parse_error_line("Opcode might be too slow for enclosing guard.",
			 p->lineno);
        
    if (p->rate == XSIG && guardrate != XSIG && xsigminrate < guardrate)
      parse_error_line("Opcode might be too slow for enclosing guard (2).",
		       p->lineno);

    return(p->rate);
  case OPARRAY:
    sym = get_opcode_decl(sa->all->g->sym,p->d->iname);
    p->defn = sym;
    p->rate = opcode_rate(sa,t,p->params,(opcode_decl *)sym->defn,p->lineno,
			  xsigminrate,guardrate);
	
    if (p->rate < guardrate)
      if (guardrate != XSIG)
	parse_error_line("Opcode too slow for enclosing guard.",
			 p->lineno);
      else if (p->rate < xsigminrate)
	parse_error_line("Opcode might be too slow for enclosing guard.",
			 p->lineno);
        
    if (p->rate == XSIG && guardrate != XSIG && xsigminrate < guardrate)
      parse_error_line("Opcode might be too slow for enclosing guard (2).",
		       p->lineno);

    return(p->rate = MAX(opcode_rate(sa,t,p->params,(opcode_decl *)sym->defn,
				     p->lineno,xsigminrate,guardrate),
			 expr_rate(sa,t,p->left,xsigminrate,guardrate)));
  case NOT:
  case UMINUS:
  case LP:
    return(p->rate = expr_rate(sa,t,p->left,xsigminrate,guardrate));
  case Q:
    r1 = expr_rate(sa,t,p->left,xsigminrate,guardrate);
    r2 = expr_rate(sa,t,p->right,xsigminrate,guardrate);
    r1 = MAX(r1,r2);
    r2 = expr_rate(sa,t,p->extra,xsigminrate,guardrate);
    return(p->rate = MAX(r1,r2));
  case LEQ:
  case GEQ:
  case EQEQ:
  case NEQ:
  case GT:
  case LT:
  case AND:
  case OR:
  case PLUS:
  case MINUS:
  case STAR:
  case SLASH:
    r1 = expr_rate(sa,t,p->left,xsigminrate,guardrate);
    r2 = expr_rate(sa,t,p->right,xsigminrate,guardrate);
    return(p->rate = MAX(r1,r2));
  default:
    interror("Bad type in expr_rate()!");
  }
  return 0;
}

long opcode_rate(sa_decoder *sa,symtable *t, exprlist *el, opcode_decl *op, int line,
		 long xsigminrate, long guardrate) {
  formalparam_list *fpl;
  long rate,fastest = 0,ct=0;
  char s[1000];

  fastest = guardrate;
  for (fpl = op->params; fpl && fpl->fp && el && el->p; fpl=fpl->next) {
    el->p->rate = rate = expr_rate(sa,t,el->p,xsigminrate,guardrate);
    if (rate > labs(fpl->fp->type)) {
      sprintf(s,"Argument #%d faster than formal parameter.",ct+1);
      parse_error_line(s,line);
    }
    if (rate > fastest) fastest = rate;
    el = el->next;
    ct++;
  }
  if (el && el->p) { /* more actual parameters; check against varargs */
    long varate = opcode_is_varargs(op->name);
    while (el) {
      /*el->p->rate = */rate = expr_rate(sa,t,el->p,xsigminrate,guardrate);
      if (rate > varate) {
	sprintf(s,"Argument #%d faster than varargs rate.",ct+1);
	parse_error_line(s,line);
      }
      if (rate > fastest) fastest = rate;
      el = el->next; ct++;
    }
  }

  if (op->type == OPCODE)
    return(fastest);
  else switch(op->type) {
    /* might need special type for downsampling core opcodes? */
  case AOPCODE:
    return ASIG;
  case KOPCODE:
    return KSIG;
  case IOPCODE:
    return IVAR;
  case SPECIALOP:
    return SPECIALOP;
  default:
    return 0;
  }
}
  
void max_rate_check_exprlist(sa_decoder *sa,symtable *t, exprlist *el, long maxrate) {

  for (;el;el=el->next)
    if (expr_rate(sa,t,el->p,IVAR, IVAR) > maxrate)
      parse_error("Parameter rate is too fast; only i-rate allowed.");
}

void check_table_def(symtable *t,tabledef *td) {

  /* check gen against list */
}
  
