/* ddomega-build.c,v 1.1 1993/09/17 22:13:49 fbodin Exp */


/*
  Functions that are used in the construction of
  an omega test problem from a source program.
  */


#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "include/portable.h"
#include "include/debug.h"
  /*
     #include "lang-interf.h"
     #include "ip.h"
  */
#include "include/affine.h"
#include "include/ddomega-build.h"
#include "include/Exit.h"


var_id *current_set_of_vars;

void init_prob(Problem *p, uint Nvars, uint Nprot,
    char *(*getVarName)(unsigned int, void *),
    void *getVarNameArgs)
{
    initializeProblem(p);

    assert(!(Nvars > maxVars));

    p->setVarsN(Nvars);
    p->_safeVars = Nprot;
    p->setNumEqs(0);
    p->setNumGEqs(0);

    p->_getVarName = getVarName;
    p->_getVarNameArgs = getVarNameArgs;
}


/* allocate a new _EQs with uninitialized co-efficients */
/*   The touched flag only makes a difference on GEQ's */

uint prob_add_EQ(Problem *p, int color)
{
    //++p->_numEQs;
    p->addNumEqs(1);
    assert(!(p->getNumEqs() > maxEQs));
    p->_EQs[p->getNumEqs() - 1].color = color;
    return p->getNumEqs() - 1;
}

/* allocate a new _EQs with all co-efficients 0 */

uint prob_add_zero_EQ(Problem *p, int color)
{
    uint c;
    //c = p->_numEQs++;
    c = p->getNumEqs();
    p->addNumEqs(1);
    assert(!(p->getNumEqs() > maxEQs));
    eqnnzero(&p->_EQs[c], p->getVarsN());
    p->_EQs[c].color = color;

    return c;
}


/* allocate a new _GEQs with uninitialized co-efficients */

uint prob_add_GEQ(Problem *p, int color)
{
    p->addNumGEqs(1);
    assert(!(p->getNumGEqs() > maxGEQs));
    p->_GEQs[p->getNumGEqs() - 1].touched = 1;
    p->_GEQs[p->getNumGEqs() - 1].color = color;
    return p->getNumGEqs() - 1;
}

/* allocate a new _GEQs with all co-efficients 0 */

uint prob_add_zero_GEQ(Problem *p, int color)
{
    uint c;
    //c = p->_numGEQs++;
    c = p->getNumGEqs();
    p->addNumGEqs(1);

    assert(!(p->getNumGEqs() > maxGEQs));
    eqnnzero(&p->_GEQs[c], p->getVarsN());
    p->_GEQs[c].touched = 1;
    p->_GEQs[c].color = color;

    return c;
}

/* delta = access1 - access2, so for flow dep, delta = write - read */

void set_deltas(Problem *p, int delta_color, range *deltas, range *a1, range *a2)
{
    uint e, c;

    assert(r_length(deltas) <= r_length(a1) && r_length(deltas) <= r_length(a2));

    for (e = 0; e < r_length(deltas); e++) {
        c = prob_add_zero_EQ(p, delta_color);
        p->_EQs[c].coef[e + r_first(deltas)] = 1;
        p->_EQs[c].coef[e + r_first(a1)] = 1;
        p->_EQs[c].coef[e + r_first(a2)] = -1;
    }
}


/*
   Constrain a problem with the minimal constraints needed to
   enforce the direction vector in dd.
   Use restraint vector unless it is not convex
   It is also tempting to use the or the direction vector instead
     if it is "=", but that does not seem to speed things up (?)
     The loss of speed is less significant if we test for eqs
     in constrain_with_convex_dddir below.
*/

void constrain_with_dd(Problem *pr, range *dd_to, range *dd_from,
    dir_and_dist_info *dd, int color)
{
    int j;

    for (j = 1; j <= dd->nest; j++) {
        int thisr;
        thisr = ddextract1(dd->restraint, j);
        if (((thisr&ddgt) && (thisr&ddlt) && !(thisr&ddeq)) /* restr = "+-" */
            || (ddextract1(dd->direction, j) == ddeq))       /* direc = "0"  */
        {
            constrain_with_convex_dddir(pr, dd_to, dd_from,
                &dd->direction, j, color);
        }
        else {
            constrain_with_convex_dddir(pr, dd_to, dd_from,
                &dd->restraint, j, color);
        }
    }
}

/*
   Constrain *pr with the DIRECTION vector in dd.
*/
void constrain_with_dddirs(Problem *pr, range *dd_to, range *dd_from,
    dir_and_dist_info *dd, int color)
{
    int j;

    for (j = 1; j <= dd->nest; j++) {
        constrain_with_convex_dddir(pr, dd_to, dd_from,
            &dd->direction, j, color);
    }
}

/*
   Constrain *pr with dimension j of *dir.
   Use equations of color "color".
*/
void constrain_with_convex_dddir(Problem *pr, range *dd_to, range *dd_from,
    dddirection *dir, int j, int color)
{
    int e;

    if (ddextract1(*dir, j) == ddeq)
    {
        e = prob_add_zero_EQ(pr, color);
        pr->_EQs[e].coef[r_first(dd_to) - 1 + j] = -1;
        pr->_EQs[e].coef[r_first(dd_from) - 1 + j] = 1;
        return;
    }
    if (!dddirtest(*dir, ddlt, j))
    {
        /* no +, so dd_to - dd_from <= 0 or < 0
           ie,  dd_from - dd_to   >= 0 or > 0
           ie,  dd_from - dd_to   >= 0 or ... -1 >= 0 */
        e = prob_add_zero_GEQ(pr, color);
        pr->_GEQs[e].coef[r_first(dd_to) - 1 + j] = -1;
        pr->_GEQs[e].coef[r_first(dd_from) - 1 + j] = 1;
        pr->_GEQs[e].coef[0] = -!dddirtest(*dir, ddeq, j);
    }
    if (!dddirtest(*dir, ddgt, j))
    {
        /* no -, so dd_to - dd_from >= 0 or > 0
           ie,    dd_to - dd_from >= 0 or ... -1 >= 0 */
        e = prob_add_zero_GEQ(pr, color);
        pr->_GEQs[e].coef[r_first(dd_to) - 1 + j] = 1;
        pr->_GEQs[e].coef[r_first(dd_from) - 1 + j] = -1;
        pr->_GEQs[e].coef[0] = -!dddirtest(*dir, ddeq, j);
    }
}



/*
  For each term in ae, add sign * its co-efficient to e.
  Declarations of symbolic constants must have been tagged with
  their column number already
  Colunm numbers of indices are their depth + r_first(indices)
  */

static void add_coefficients(Eqn e, range *indices, int sign, affine_expr *ae)
{
    int v;

    assert(ae->terms[0].tiny_var == NIL);
    e->coef[0] += sign * ae->terms[0].coefficient;

    for (v = 1; v < ae->nterms; v++) {
        if (var_id_index_p(ae->terms[v].tiny_var)) { /* index */
            int loop = var_ids_loop_no(ae->terms[v].tiny_var);
            e->coef[r_first(indices) - 1 + loop] +=
                sign * ae->terms[v].coefficient;
        }
        else { /* sym. const */
            assert(var_id_const_p(ae->terms[v].tiny_var));
            e->coef[var_ids_tag(ae->terms[v].tiny_var)] +=
                sign * ae->terms[v].coefficient;
        }
    }
}



/* for the lower bound, lb <= i ===  i - lb >= 0
   for the upper bound, i <= ub === -i + ub >= 0
   define bound types so that establish_one_bound works out...
   */

typedef enum { upper = 1, lower = -1 } which_bound;


/* return 1 if boundable, 0 if not affine */

static int
establish_one_bound(Problem *p, int color,
    range *indices, which_bound b,
    affine_expr *ae, uint depth)
{
    if (!is_affine(ae))
        return 0;

    while (ae != NIL) {
        uint c;  /* new constraint */

        assert(is_affine(ae));

        c = prob_add_zero_GEQ(p, color);
        add_coefficients(&p->_GEQs[c], indices, b, ae);
        //assert(p->_GEQs[c].coef[r_first(indices) - 1 + depth] == 0);
        if (p->_GEQs[c].coef[r_first(indices) - 1 + depth] != 0)
            return 0;

        p->_GEQs[c].coef[r_first(indices) - 1 + depth] = -b;
        ae = ae->other_branch;
    }
    return 1;
}


/*
  start_expr should point to the 1st expression taken on by the loop
  return 1 if bound, 0 if not
  */

static int
handle_nonunit_increments(Problem *p, range *indices, int *which_step, int color, loop_context l, sint incr, uint depth)
{
    affine_expr *start_expr = loop_start(l);

    assert(incr >= 1);  /* can't handle any others yet */

    if (abs(incr) == 1)
        return 1;

    /* if lower bound is linear AND HAS NO "MAX"
        add the constraint:   index = lower_bound + gensym * step
        otherwise, don't add any new constraints, unless color == red */

    assert(start_expr != NIL);

    if (is_affine(start_expr) && start_expr->other_branch == NIL)
    {
        int c, v, i2;
        c = prob_add_zero_EQ(p, color);
        add_coefficients(&p->_EQs[c], indices, 1, start_expr);

        /* we now have the EQ "lower_bound = 0" */

        //assert(p->_EQs[c].coef[r_first(indices) - 1 + depth] == 0);
        if (p->_EQs[c].coef[r_first(indices) - 1 + depth] != 0)
            return 0;

        p->_EQs[c].coef[r_first(indices) - 1 + depth] = -1;

        /* we now have the EQ "lower_bound - index = 0" */

        /* we can reduce the co-efficients to their value mod(step),
           since we're adding gensym()*step */

        i2 = incr / 2;
        for (v = 1; v <= p->getVarsN(); v++) {
            int tmp = intMod(p->_EQs[c].coef[v], incr);
            if (tmp > i2) tmp -= incr;
            p->_EQs[c].coef[v] = tmp;
        }

        p->_EQs[c].coef[*which_step] = incr;
        (*which_step)++;

        return 1;
    }
    else {
        return 0;
    }
}

/* Create constraints in prob for the loop index whose dolimit
   is pointed to by limits.
   May add one or more(min or max) upper and lower constraints
   May add an equality constraint if the increment != 0

   return 1 if completely bound, 0 if we had any non-affine stuff
*/

static int bound_index(Problem *p, int color, loop_context limits, range *indices, range *non_i, int *which_step, uint depth)
{
    sint incr;
    bool known;

    incr = 1; known = 1;

    if (loop_has_step_p(limits)) {
        /* there is an increment expression */
        loops_step(limits, &incr, &known);
    }

    if (!(known && incr >= 1))
        ErrAssert("DO loop step must be positive");

    if (known) {
        if (incr > 0) {
            int l, u, i;
            l = establish_one_bound(p, color, indices, lower, loop_start(limits), depth);
            u = establish_one_bound(p, color, indices, upper, loop_end(limits), depth);
            i = handle_nonunit_increments(p, indices, which_step, color, limits, incr, depth);
            return l && u && i;
        }
        else if (incr < 0) {
            /* assertion checking is off */
            int l, u, i;

            fprintf(stderr, "can't handle negative step yet\n");
            Exit(2);

            l = establish_one_bound(p, color, indices, lower, loop_end(limits), depth);
            u = establish_one_bound(p, color, indices, upper, loop_start(limits), depth);
            i = handle_nonunit_increments(p, indices, which_step, color, limits, incr, depth);
            return l && u && i;
        }
        else {
            /* assertion checking is off */
            fprintf(stderr, "What do you mean by having a step of 0?  What are you, stupid?\n");
            Exit(1);
            return 0;
        }
    }
    else {
        /* assertion checking is off */
        fprintf(stderr, "can't handle unknown step\n");
        Exit(2);
        return 0;
    }
}


/* Create constraints in problem for the conditional.
   May add any number of GEQ or EQ constraints

   return 1 if completely bound, 0 if we had any non-affine stuff

   This initial version only works with the puny subset of conditionals
   described in affine.c, and is tiny-specific.
   A conservative tiny-free alternative is to just return 0.
*/

static struct {
    int lhs_sign;
    int rhs_sign;
    int constant;
} conditionals[4] = {
    { 1, -1, -1 },  /* gt */
    { 1, -1, 0  },  /* ge */
    { -1, 1, -1 },  /* lt */
    { -1, 1, 0  }   /* le */
};

#define N_ELEMENTS(ARRAY)   ( sizeof(ARRAY)/sizeof(ARRAY[0]) )
static int
puny_bound_condition(Problem *p, int color, if_context cond,
    range *indices, range *non_i)
{
    if (if_condition_ok(cond))
    {
        int c, kind;
        kind = if_compare_op(cond);

        if (kind < 0 || kind >= N_ELEMENTS(conditionals)) {
            assert(!"What the heck?  How did that get to be affine?");
            return 0;
        }

        if (if_else_branch(cond)) {
            kind = N_ELEMENTS(conditionals) - kind;
        }
        else {
#if ! defined NOT_TINY
            assert(cond->nodeop == op_then ||
                cond->nodeparent->nodeop == op_assert);
#endif
        }

        c = prob_add_zero_GEQ(p, color);
        add_coefficients(&p->_GEQs[c], indices, conditionals[kind].lhs_sign,
            if_compare_left(cond));
        add_coefficients(&p->_GEQs[c], indices, conditionals[kind].rhs_sign,
            if_compare_right(cond));
        p->_GEQs[c].coef[0] += conditionals[kind].constant;

#if ! defined NOT_TINY
        assert(!cond->nodeparent->nodeaffine->other_branch->other_branch);
#endif
        return 1;
    }
    else {
        return 0;
    }
}


/* Establish bounds for the loop indices and conditionals
   that enclose node n.
   If we come across a non-affine expression return 0.
   If we successfully bound everything       return 1.
   Note that we finish doing all the bounds we can,
   in either case, so zapping will work well.

   Assume that "outer" outer loops have been taken care of
   in some other way - i.e. that we are building the red
   part of some problem which represents a dependence with
   "outer" leading 0's, so we will take care of these
   variables by setting them equal to the black loop indices.

   If "skip_outer_ifs_containing_me" is non-nil, any ifs
   that are within exactly "outer" loops and contain it.

   This corresponds to the situation:
     for1
       for2
         if1 then
       if2a then
             access a
           endif
       if2b then
             access skip_outer_ifs_containing_me
           endif
         endif
       endfor
     endfor

   in which we want to skip if1 when bounding a in the 0,0 dependence
   from a to skip_outer_ifs_containing_me.  Note that we don't have to
   distinguish between the then & else parts, as we can't have a 0,0
   if one access is is each.
 */

int
bound_inner_indices_and_conditionals(Problem *p,
    range *indices, range *steps, range *non_i,
    int outer, a_access skip_outer_ifs_containing_me,
    int color, a_access a)
{
    int result;
    int which_step = r_first(steps);
    context_iterator c;

    if (a == Entry || a == ExitNode)
        return(1);

    c = cont_i_for_access(a);
    result = 1;

    while (!cont_i_done(c))
    {
        if (cont_i_cur_loop_p(c)) {
            if (cont_i_cur_depth(c) <= outer)
                return result;

#if ! defined NOT_TINY
            assert(do_op(c->nodeparent->nodeop));
#endif

            if (!bound_index(p, color, cont_i_cur_loop(c),
                indices, non_i, &which_step,
                cont_i_cur_depth(c)))
            {
                assert(which_step <= r_last(steps) + 1);
                result = 0;
            }
        }
        else {
            assert(cont_i_cur_if_p(c));
#if ! defined NOT_TINY
            assert(c->nodeparent->nodeop == op_if);
#endif
            if (!(skip_outer_ifs_containing_me
                && cont_i_cur_depth(c) <= outer
                && access_is_in_then_or_else_of(skip_outer_ifs_containing_me, c))
                && !puny_bound_condition(p, color, cont_i_cur_if(c),
                    indices, non_i))
            {
                result = 0;
            }
        }

        cont_i_next(c);
    }
    assert(which_step <= r_last(steps) + 1);

#if ! defined NOT_TINY
    for (c = Assertions; c; c = c->nodelink)
    {
        assert(c->nodeop == op_assert);
        if (!puny_bound_condition(p, color, c->nodechild, indices, non_i))
        {
            result = 0;
        }
    }
#endif

    return result;
}


/* adjust the problem to include equality of subscript expressions at
   nodes access1 and access2.  Index variables for access1 are looked
   up in range i1, and those for access2 in range i2.  The non-index
   variables are looked up in the range non_i.

   returns 0 if subscripts could not possibly be equal.
             In this case, we may not finish adding the equalities.
   returns 1 if the conditions for their equality have been completely
             described by the constraints added to p
   returns 2 if there was a non-affine expression, in which case the
             constraints added to p must be true for equality (but they
         may also be true for some non-equal cases).  If color==red,
         we FINISH trying to bound subscripts (for zapping).
   */

equate_descr equate_subscripts(Problem *p, range *i1, range *i2, range *non_i,
    int color, a_access access1, a_access access2)
{
    sub_iterator sub1, sub2;
    int is_complete = complete;

    if (access1 == Entry || access1 == ExitNode ||
        access2 == Entry || access2 == ExitNode)
        return (equate_descr)1;

    /* we assume children of the node are subscripts,
       so scalars MUST have no nodechildren
    assert(array_op(access1->nodeop) || access1->nodechild == NIL);
    */

    sub1 = sub_i_for_access(access1);
    sub2 = sub_i_for_access(access2);

    while (!sub_i_done(sub1))
    {
        assert(!sub_i_done(sub2));  /* must have same # of subscripts */
        assert(sub_i_cur_affine(sub1) != NIL && sub_i_cur_affine(sub2) != NIL);

        if (sub_i_cur_is_affine(sub1) && sub_i_cur_is_affine(sub2)) {
            uint c;  /* new constraint # */
            int i;

            assert((sub_i_cur_affine(sub1)->other_branch == NIL) &&
                (sub_i_cur_affine(sub2)->other_branch == NIL));

            c = prob_add_zero_EQ(p, color);
            add_coefficients(&p->_EQs[c], i1, 1, sub_i_cur_affine(sub1));
            add_coefficients(&p->_EQs[c], i2, -1, sub_i_cur_affine(sub2));

            for (i = p->getVarsN(); i > 0; i--)
                if (p->_EQs[c].coef[i] != 0)
                    break;
            if (i == 0) {   /* linearity = 0 */
                if (p->_EQs[c].coef[0] != 0)
                    return not_possible;   /* never equal */
                //p->_numEQs--;   /* always equal: don't add equation */
                p->addNumEqs(-1);
            }
        }
        else {
            is_complete = partial;
        }
        sub_i_next(sub1);
        sub_i_next(sub2);
    }

    assert(sub_i_done(sub2));  /* must have same # of subscripts */
    return (equate_descr)is_complete;
}

/* postcondition: n < maxVars */
static void NeedNVars(int n)
{
    if (n >= maxVars) {
        fprintf(debug2, "Out of Omega variables\n");
        Exit(2);
    }
}

/* set bounds[1...depth(n)] to point to the dolimits of the loops containing n
   set *Nsteps to the # of loops that have step expressions
   set steps[0  ... *Nsteps - 1] to NIL
   */

void load_bounds_and_count_steps(a_access a, var_id bounds[],
    var_id steps[], int *Nsteps)
{
    context_iterator c;

    if (a == Entry || a == ExitNode)
        return;

    c = cont_i_for_access(a);
    while (!cont_i_done(c))
    {
        if (cont_i_cur_loop_p(c)) {
            bounds[cont_i_cur_depth(c)] = loop_var_id(cont_i_cur_loop(c));
            if (loop_has_step_p(cont_i_cur_loop(c))) {
                /* handle step */
                NeedNVars(*Nsteps);
                steps[*Nsteps] = 0;
                (*Nsteps)++;
            }
        }
        else {
            assert(cont_i_cur_if_p(c));
            /* handle if trivially - no new bounds or steps */
        }

        cont_i_next(c);
    }
}

typedef struct {
    var_id *consts;
    int    *Nconsts;
} const_stuff;

static void load_a_const(var_id v, void *args)
{
    const_stuff *cs = (const_stuff *)args;

    if (var_id_const_p(v)) {
        /* not a loop index */
        if (var_ids_tag(v) == UNTAGGED)
        {
            NeedNVars(*cs->Nconsts);
            var_ids_tag(v) = *cs->Nconsts;
            cs->consts[(*cs->Nconsts)++] = v;
        }
        else {
            assert(cs->consts[var_ids_tag(v)] == v);
        }
    }
    else {
        assert(var_id_index_p(v));
    }
}

#if 0
static void load_consts_for_expr(node *expr, var_id consts[], int *Nconsts)
{
    node *nn;

    for (nn = expr; nn != NIL; nn = nn->nodenext) {
        assert(!array_op(nn->nodeop));
        switch (nn->nodeop) {
        case op_fetch:
        case op_store:
            if (get_nodevalue_node(nn)->nodeop == op_declare) {
                /* not a loop index */
                if (get_nodevalue_sym(get_nodevalue_node(nn))->symtag
                    == UNTAGGED)
                {
                    NeedNVars(*Nconsts);
                    get_nodevalue_sym(get_nodevalue_node(nn))->symtag
                        = *Nconsts;
                    consts[(*Nconsts)++] =
                        get_nodevalue_sym(get_nodevalue_node(nn));
                }
                else {
                    assert(consts[get_nodevalue_sym(get_nodevalue_node(nn))
                        ->symtag]
                        == get_nodevalue_sym(get_nodevalue_node(nn)));
                }
            }
            else {
                assert(get_nodevalue_node(nn)->nodeop == op_dolimit);
            }
            break;
        default:
            load_consts_for_expr(nn->nodechild, consts, Nconsts);
        }
    }
}

#endif

/* ensure that all symbolic constants used in affine subscript
   expressions appear in consts[0..*Nconsts-1], and that they
   are tagged with their indices.
   If called with a scalar, do nothing
   */
void load_constants_for_subscripts(a_access a, var_id consts[], int *Nconsts)
{
    const_stuff cs;
    sub_iterator sub;

    if (a == Entry || a == ExitNode)
        return;

    cs.consts = consts;
    cs.Nconsts = Nconsts;

    sub = sub_i_for_access(a);
    while (!sub_i_done(sub)) {
        sub_i_map_over_cur_vars(sub, &load_a_const, &cs);
        sub_i_next(sub);
    }
}

/* same for affine expressions used in loop bounds of loops surrounding n */
void load_constants_for_bounds(a_access a, var_id consts[], int *Nconsts)
{
    const_stuff cs;
    context_iterator c;

    if (a == Entry || a == ExitNode)
        return;

    cs.consts = consts;
    cs.Nconsts = Nconsts;

    c = cont_i_for_access(a);
    while (!cont_i_done(c))
    {
        if (cont_i_cur_loop_p(c)) {
            loop_map_over_start_vars(cont_i_cur_loop(c), &load_a_const, &cs);
            loop_map_over_end_vars(cont_i_cur_loop(c), &load_a_const, &cs);
        }
        else {
            assert(cont_i_cur_if_p(c));
            if_map_over_vars(cont_i_cur_if(c), &load_a_const, &cs);
        }

        cont_i_next(c);
    }

#if ! defined NOT_TINY
    for (c = Assertions; c; c = c->nodelink) {
        if_map_over_vars(c->nodechild, &load_a_const, &cs);
    }
#endif
}
