This commit is contained in:
2025-03-12 12:37:19 +03:00
committed by Dudarenko
parent 0c9f0664fd
commit d4fb323f86
428 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,730 @@
/* kill.c,v 1.1 1993/09/17 22:13:59 fbodin Exp */
/*
Dependence kill tests ("quick" and "accurate")
Naming convention: Many of these functions and structures
refer to "read iteration" or "write iteration" as if a
test were being performed on flow dependence(s), even when
the test works for other forms of dependence.
*/
#include <assert.h>
#include <string.h>
#include "include/portable.h"
#include <stdio.h>
#include <stdlib.h>
#include "include/debug.h"
#include "include/ip.h"
#include "include/lang-interf.h"
#include "include/ddomega-build.h"
#include "include/ddomega-use.h"
#include "include/kill.h"
#include "include/missing.h"
#include "include/timeTrials.h"
#include "include/Exit.h"
/* The following table shows the possible results
of combining two direction vectors:
! + 0 0+ - +- 0- *
! ! ! ! ! ! ! ! !
+ ! + + + * * * *
0 ! + 0 0+ - +- 0- *
0+ ! + 0+ 0+ * * * *
- ! * - * - * - *
+- ! * +- * * * * *
0- ! * 0- * - * 0- *
* ! * * * * * * *
*/
static int ddCombine[8][8] =
{ 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 7, 7, 7, 7,
0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 3, 3, 7, 7, 7, 7,
0, 7, 4, 7, 4, 7, 4, 7,
0, 7, 5, 7, 7, 7, 7, 7,
0, 7, 6, 7, 4, 7, 6, 7,
0, 7, 7, 7, 7, 7, 7, 7 };
/*
Test to see if flow dependence f (from middle_acc to to_acc) kills
the flow dependence dd (from from_acc to to_acc)
via the output dependence o (from from_acc to middle_acc).
*/
static dddirection
do_test_for_kill_via(dir_and_dist_info *dd, char *dd_as_string,
a_access from_acc, a_access middle_acc, a_access to_acc,
dd_current f, dd_current o,
int commonDepth)
{
read_prob_desc rpd; /* write1s = A[i]
write2s = B[j]
reads = C[k]
we project onto i and k, so it doesn't
matter that either A or C or neither
could be the read */
Problem reads;
dir_and_dist_info tmp_ddi;
var_id sc_vars[maxVars], r_vars[maxVars], rs_vars[maxVars];
var_id w1_vars[maxVars], w1s_vars[maxVars];
var_id w2_vars[maxVars], w2s_vars[maxVars];
int from_depth, middle_depth, to_depth;
int Nrs, Nw1s, Nw2s, Nsc;
int j, red_complete;
#if ! defined NDEBUG
int eqs, geqs;
#endif
assert(access_store_p(middle_acc) || access_update_p(middle_acc));
from_depth = accesss_depth(from_acc);
middle_depth = accesss_depth(middle_acc);
to_depth = accesss_depth(to_acc);
#if defined newTimeTrials
if (storeResult) killTests++;
#endif
#ifndef SPEED
if (omegaPrintResult) {
fprintf(debug2, "continuing check for KILL\n");
fprintf(debug2, "Does %s\n", dd_current_as_string(f));
fprintf(debug2, "Kill %s\n", dd_as_string);
fprintf(debug2, "via %s\n", dd_current_as_string(o));
}
#endif
/* Quick check to see if direction vectors are feasible */
assert(commonDepth <= dd_current_nest(o));
for (j = 1; j <= commonDepth; j++) {
if (dd->distanceKnown[j] &&
dd_current_dist_known(f, j) &&
dd_current_dist_known(o, j))
{
if (dd_current_dist(f)[j] + dd_current_dist(o)[j] !=
dd->distance[j])
break;
}
else {
int m = ddCombine[ddextract1(dd_current_dir(f), j)]
[ddextract1(dd_current_dir(o), j)];
int t = ddextract1(dd->direction, j);
if ((m & t) != t) break;
}
}
if (j <= commonDepth) { /* break occured */
if (omegaPrintResult) {
fprintf(debug2,
"kill not feasible because of dddir at level %d\n",
j);
}
return 0;
}
/* PART 1: find sets of variables to be used in problem */
Nrs = Nw1s = Nw2s = Nsc = 0;
load_bounds_and_count_steps(to_acc, r_vars, rs_vars, &Nrs);
load_bounds_and_count_steps(from_acc, w1_vars, w1s_vars, &Nw1s);
load_bounds_and_count_steps(middle_acc, w2_vars, w2s_vars, &Nw2s);
load_constants_for_bounds(to_acc, sc_vars, &Nsc);
load_constants_for_bounds(from_acc, sc_vars, &Nsc);
load_constants_for_bounds(middle_acc, sc_vars, &Nsc);
load_constants_for_subscripts(to_acc, sc_vars, &Nsc);
load_constants_for_subscripts(from_acc, sc_vars, &Nsc);
load_constants_for_subscripts(middle_acc, sc_vars, &Nsc);
/* PART 2: assign columns to variables
Protect variables representing symbolic constants,
read iteration and 1st write values of loop indices,
because we wish to test if S implies T
for all values of them. */
read_init(&rpd, &reads, sc_r_and_w1, Nsc, sc_vars,
to_depth, r_vars, Nrs, rs_vars,
from_depth, w1_vars, Nw1s, w1s_vars,
middle_depth, w2_vars, Nw2s, w2s_vars);
/* PART 3: build problem */
#if !defined NDEBUG
read_inv(&rpd, &reads);
assert(reads.getNumEqs() == 0);
assert(reads.getNumGEqs() == 0);
eqs = geqs = 0;
#endif
/* TRY TO SET UP RED "T" PROBLEM:
"j in [B] ^ A(i) << B(j) << C(k) ^ A(i) sub= B(j) sub= C(k)"
If C is not exit, we can omit A(i) sub= B(j), as it is
redundant with B(j) sub= C(k) and A(i) sub= C(k)
IF WE CAN'T ESTABLISH COMPLETE RED BOUNDS, DON'T KILL */
/* j in [B] */
red_complete =
bound_indices_and_conditionals(&reads, &rpd.write2s,
&rpd.w2steps, &rpd.nonloops,
red, middle_acc);
if (to_acc != ExitNode) {
/* B(j) sub= C(k) */
/* equate_subs can't return 0 */
red_complete = red_complete &&
equate_subscripts(&reads, &rpd.reads, &rpd.write2s,
&rpd.nonloops,
red, to_acc, middle_acc) == complete;
}
else {
/* B(j) sub= A(i) */
/* equate_subs can't return 0 */
red_complete = red_complete &&
equate_subscripts(&reads, &rpd.write1s, &rpd.write2s,
&rpd.nonloops,
red, from_acc, middle_acc) == complete;
}
if (!red_complete)
{
if (omegaPrintResult) {
fprintf(debug2, "not checking KILL - incomplete red bounds \n");
}
read_cleanup(&rpd);
return 0;
}
/* B(j) << C(k) */
ddnode_to_dir_and_dist(f, &tmp_ddi);
constrain_with_dd(&reads, &rpd.reads, &rpd.write2s, &tmp_ddi, red);
/* A(i) << B(j) */
ddnode_to_dir_and_dist(o, &tmp_ddi);
constrain_with_dd(&reads, &rpd.write2s, &rpd.write1s, &tmp_ddi, red);
#if !defined NDEBUG
read_inv(&rpd, &reads);
for ( /* eqs as it is */; eqs < reads.getNumEqs(); eqs++)
{
assert(reads._EQs[eqs].color == red);
}
for ( /* geqs as it is */; geqs < reads.getNumGEqs(); geqs++)
{
assert(reads._GEQs[geqs].color == red);
}
#endif
/* SET UP BLACK "S" PROBLEM:
"i in [A] ^ k in [C] ^ A(i) << C(k) ^ A(i) sub= C(k)" */
/* i in [A] */
bound_indices_and_conditionals(&reads, &rpd.write1s, &rpd.w1steps,
&rpd.nonloops, black, from_acc);
/* k in [C] */
bound_indices_and_conditionals(&reads, &rpd.reads, &rpd.rsteps,
&rpd.nonloops, black, to_acc);
/* A(i) sub= C(k) */
/* this must be possible, or there would not be a dependence */
equate_subscripts(&reads, &rpd.reads, &rpd.write1s, &rpd.nonloops,
black, to_acc, from_acc);
/* A(i) << C(k) */
constrain_with_dd(&reads, &rpd.reads, &rpd.write1s, dd, black);
#if !defined NDEBUG
for (/* eqs as it is */; eqs < reads.getNumEqs(); eqs++) {
assert(reads._EQs[eqs].color == black);
}
for (/* geqs as it is */; geqs < reads.getNumGEqs(); geqs++) {
assert(reads._GEQs[geqs].color == black);
}
#endif
/* PART 4: clean up */
#if ! defined NDEBUG
read_inv(&rpd, &reads);
#endif
read_cleanup(&rpd);
/* PART 5: CHECK FOR KILL: Forall i,k,Sym, does S imply T? */
if (omegaPrintResult) {
fprintf(debug2, "checking for KILL in: \n");
printProblem(&reads);
}
#if defined newTimeTrials
if (storeResult) realKillTests++;
#endif
/* There must be a solution to the black equations,
because they describe a flow dependence from A to C */
if (!hasRedEquations(&reads, 0)) {
if (omegaPrintResult) {
fprintf(debug2, "verified KILL:\n");
printProblem(&reads);
}
#if defined newTimeTrials
if (storeResult) realKills++;
#endif
return ddkilled;
}
else {
if (omegaPrintResult) {
fprintf(debug2, "no KILL:\n");
printProblem(&reads);
}
return 0;
}
}
/*
Test to see if flow dependence dd2 (from write_acc2 to read_acc) kills
the flow dependence dd1 (frow write_acc1 to read_acc).
This should be called if quick_test_for_kill returns 0.
*/
dddirection
accurate_test_for_kill(dir_and_dist_info *dd, char *dd_as_string,
a_access from_acc, a_access to_acc,
dd_current this_dep)
{
dd_in_iterator potential_killers;
#ifndef SPEED
if (omegaPrintResult) {
fprintf(debug2, "starting check for KILL of dependence:\n");
fprintf(debug2, "%s\n", dd_as_string);
}
#endif
potential_killers = dd_i_i_for_access(to_acc);
while (!dd_i_i_done(potential_killers))
{
/* search for intervening writes or different updates */
if ((access_store_p(dd_i_i_cur_src(potential_killers)) ||
(access_update_p(dd_i_i_cur_src(potential_killers)) &&
!access_same_update_type_p(dd_i_i_cur_src(potential_killers),
to_acc) &&
!access_same_update_type_p(dd_i_i_cur_src(potential_killers),
from_acc))) &&
!dd_i_i_cur_is(potential_killers, this_dep))
{
dd_out_iterator o;
int commonDepth;
commonDepth = MIN(dd->nest, dd_current_nest(dd_i_i_current(potential_killers)));
o = dd_o_i_for_access(from_acc);
while (!dd_o_i_done(o))
{
if (dd_i_i_cur_src(potential_killers) == dd_o_i_cur_dest(o))
{
/* test for kill with output dep "o" */
dddirection d;
d = do_test_for_kill_via(dd, dd_as_string,
from_acc, dd_o_i_cur_dest(o),
to_acc,
dd_i_i_current(potential_killers),
dd_o_i_current(o), commonDepth);
if (d)
return d;
}
dd_o_i_next(o);
}
}
dd_i_i_next(potential_killers);
}
return 0;
}
/* The following table shows whether dd1 is strictly < dd2,
strictly <= dd2,
or possibly > dd2.
It is used in dd_is_shorter
If + in dd1, dd1 p> dd2
else if 0 in dd1, if - in dd2, dd1 p> dd2
else if dd2 == +, dd1 < dd2
else (0 or 0+) dd1 <= dd2
else (dd1 == -) if - in dd2, dd1 p> dd2
else dd1 < dd2
*/
typedef enum { pg = 0, le, lt } ddComparison;
/* compare[dd1][dd2] */
ddComparison ddCompare[8][8] =
{
/* DD2 + 0 0+ - +- 0- * */
/* DD1 */
/* */ { pg, pg, pg, pg, pg, pg, pg, pg },
/* + */ { pg, pg, pg, pg, pg, pg, pg, pg },
/* 0 */ { pg, lt, le, le, pg, pg, pg, pg },
/* 0+ */ { pg, pg, pg, pg, pg, pg, pg, pg },
/* - */ { pg, lt, lt, lt, pg, pg, pg, pg },
/* +- */ { pg, pg, pg, pg, pg, pg, pg, pg },
/* 0- */ { pg, lt, le, le, pg, pg, pg, pg },
/* * */ { pg, pg, pg, pg, pg, pg, pg, pg }
};
/* if possibly_shorter is strictly shorter than possibly_longer, return true
if possibly_shorter might be scum than possibly_longer, return false
if possibly_shorter is shorter or of equal length, return what_if_equal
possibly_shorter is assumed to be a covering dependence to the
same destination as possibly_longer, and thus possibly_longer
may be "partially refined".
*/
static int
dd_is_shorter(dd_current possibly_shorter,
dir_and_dist_info *possibly_longer,
int what_if_equal)
{
ddComparison c;
int j;
int minLength = MIN(possibly_longer->nest, dd_current_nest(possibly_shorter));
for (j = 1; j <= minLength; j++)
{
if (dd_current_dist_known(possibly_shorter, j) &&
possibly_longer->distanceKnown[j])
{
c = dd_current_dist(possibly_shorter)[j] < possibly_longer->distance[j] ? lt :
(dd_current_dist(possibly_shorter)[j] == possibly_longer->distance[j] ? le : pg);
}
else {
c = ddCompare[ddextract1(dd_current_dir(possibly_shorter), j)]
[ddextract1(possibly_longer->direction, j)];
}
switch (c) {
case lt:
return 1;
case pg:
return 0;
break;
case le:
if (!dddirtest(dd_current_dir(possibly_shorter), ddlt, j)) {
if (dddirtest(possibly_longer->direction, ddlt, j)) {
if (!(possibly_longer->direction & ddrefined))
{
clone_dd_graph_node_for_refinement(possibly_longer->dd_graph_node_to_be_cloned);
possibly_longer->direction |= ddrefined;
}
dddirreset(possibly_longer->direction, ddlt, j);
d_info_do_eq(possibly_longer, j);
};
if (!dddirtest(dd_current_dir(possibly_shorter), ddeq, j)
&& dddirtest(possibly_longer->direction, ddeq, j))
{
if (!(possibly_longer->direction & ddrefined))
{
clone_dd_graph_node_for_refinement(possibly_longer->dd_graph_node_to_be_cloned);
possibly_longer->direction |= ddrefined;
}
dddirreset(possibly_longer->direction, ddeq, j);
};
};
break;
}
}
/* le in all directions if we get here. */
return what_if_equal;
}
/*
Do quick but possibly indecisive kill tests to see
if dependence dd (from write_acc2 to read_acc) kills
the flow dependence dd1 (frow write_acc1 to read_acc).
If dd1 is obviously killed by dd2, this function will return
either ddiscovered or ddisterminated.
Otherwise, this function returns 0 (is which case dd2 may
or may not be killed - call accurate_test_for_kill to find out).
This function may update dd1 if dd1 is partly killed by dd2.
In this case, dd1's ddrefined bit will be set.
*/
dddirection
quick_test_for_kill(dir_and_dist_info *dd, char *dd_as_string,
a_access from_acc, a_access to_acc,
dd_current this_dep)
{
dd_in_iterator covers_dest; /* search for a dd that covers to_acc */
dd_out_iterator terms_src; /* search for a dd that terminates from_acc */
dddirection oldDirection = dd->direction;
assert(!ddisDead(dd->direction));
#ifndef SPEED
if (omegaPrintResult) {
fprintf(debug2, "starting quick kill check for:\n");
fprintf(debug2, "%s\n", dd_as_string);
}
#endif
covers_dest = dd_i_i_for_access(to_acc);
while (!dd_i_i_done(covers_dest))
{
a_access cur_src = dd_i_i_cur_src(covers_dest);
#if ! defined NOT_TINY
assert(covers_dest->ddsucc == to_acc);
#endif
if (!dd_i_i_cur_is(covers_dest, this_dep) &&
dd_i_i_cur_cover_p(covers_dest) &&
dd_is_shorter(dd_i_i_current(covers_dest), dd,
(accesss_shared_depth(from_acc, cur_src)
== dd->nest) &&
access_lexically_preceeds(from_acc, cur_src)))
{
if (omegaPrintResult) {
fprintf(debug2,
"Yes: there is a closer cover of the destination:\n");
fprintf(debug2, "%s\n",
dd_current_as_string(dd_i_i_current(covers_dest)));
}
return ddisCovered;
}
dd_i_i_next(covers_dest);
}
terms_src = dd_o_i_for_access(from_acc);
while (!dd_i_i_done(terms_src))
{
a_access cur_dest = dd_o_i_cur_dest(terms_src);
#if ! defined NOT_TINY
assert(terms_src->ddpred == from_acc);
#endif
if (!dd_o_i_cur_is(terms_src, this_dep) &&
dd_o_i_cur_terminate_p(terms_src) &&
dd_is_shorter(dd_o_i_current(terms_src), dd,
(accesss_shared_depth(to_acc, cur_dest)
== dd->nest) &&
access_lexically_preceeds(cur_dest, to_acc)))
{
if (omegaPrintResult) {
fprintf(debug2,
"Yes: there is a closer terminator of the source:\n");
fprintf(debug2, "%s\n",
dd_current_as_string(dd_i_i_current(terms_src)));
}
return ddisTerminated;
}
dd_o_i_next(terms_src);
}
if (omegaPrintResult) {
if (oldDirection != dd->direction)
fprintf(debug2, "direction vector refined but not killed\n");
else fprintf(debug2, "quick kill check indecisive\n");
}
return 0;
}
/* functions for manipulating "read problem descriptions" */
/* name information for buffers in getVarName fns */
#define MaxNameLen 254
#define MaxSuffixLen 1
#if !defined NDEBUG
static int var_id_is_step_expr(var_id n)
{
return n == 0;
}
void read_inv(read_prob_desc *rpd, Problem *p)
{
int v;
assert(p->getVarsN() < maxVars);
assert(p->getVarsN() >= read_Nvars(rpd));
assert(r_first(&rpd->nonloops) == 1);
assert(r_last(&rpd->nonloops) + 1 == r_first(&rpd->reads));
assert(r_last(&rpd->reads) + 1 == r_first(&rpd->rsteps));
assert(r_last(&rpd->rsteps) + 1 == r_first(&rpd->write1s));
assert(r_last(&rpd->write1s) + 1 == r_first(&rpd->w1steps));
assert(r_last(&rpd->w1steps) + 1 == r_first(&rpd->write2s));
assert(r_last(&rpd->write2s) + 1 == r_first(&rpd->w2steps));
for (v = 0; v < r_length(&rpd->nonloops); v++) {
assert(rpd->vars[v + r_first(&rpd->nonloops)] != NIL);
assert(var_id_const_p(rpd->vars[v + 1]));
assert(var_ids_tag(rpd->vars[v + 1]) == v + 1);
}
for (v = 0; v < r_length(&rpd->reads); v++) {
assert(rpd->vars[v + r_first(&rpd->reads)] != NIL);
assert(var_id_index_p(rpd->vars[v + r_first(&rpd->reads)]));
assert(var_ids_loop_no(rpd->vars[v + r_first(&rpd->reads)]) == v + 1);
}
for (v = 0; v < r_length(&rpd->rsteps); v++) {
assert(var_id_is_step_expr(rpd->vars[v + r_first(&rpd->rsteps)]));
}
for (v = 0; v < r_length(&rpd->write1s); v++) {
assert(rpd->vars[v + r_first(&rpd->write1s)] != NIL);
assert(var_id_index_p(rpd->vars[v + r_first(&rpd->write1s)]));
assert(var_ids_loop_no(rpd->vars[v + r_first(&rpd->write1s)]) == v + 1);
}
for (v = 0; v < r_length(&rpd->w1steps); v++) {
assert(var_id_is_step_expr(rpd->vars[v + r_first(&rpd->w1steps)]));
}
for (v = 0; v < r_length(&rpd->write2s); v++) {
assert(rpd->vars[v + r_first(&rpd->write2s)] != NIL);
assert(var_id_index_p(rpd->vars[v + r_first(&rpd->write2s)]));
assert(var_ids_loop_no(rpd->vars[v + r_first(&rpd->write2s)]) == v + 1);
}
for (v = 0; v < r_length(&rpd->w2steps); v++) {
assert(var_id_is_step_expr(rpd->vars[v + r_first(&rpd->w2steps)]));
}
for (v = 0; v < p->getNumGEqs(); v++) {
assert(p->_GEQs[v].touched);
}
}
#endif
static char *read_getVarName(uint v, void *args)
{
read_prob_desc *rpd = (read_prob_desc*)args;
static char name[MaxNameLen + MaxSuffixLen + 1];
assert(v <= read_Nvars(rpd));
if (rpd->vars[v] &&
(var_id_const_p(rpd->vars[v]) || var_id_index_p(rpd->vars[v]))) {
strncpy(name, var_ids_name(rpd->vars[v]), MaxNameLen);
name[MaxNameLen] = 0;
}
else {
assert(var_id_is_step_expr(rpd->vars[v]));
strcpy(name, "<trip>");
}
if (r_in(&rpd->reads, v) || r_in(&rpd->rsteps, v))
strcat(name, "0");
else if (r_in(&rpd->write1s, v) || r_in(&rpd->w1steps, v))
strcat(name, "1");
else if (r_in(&rpd->write2s, v) || r_in(&rpd->w2steps, v))
strcat(name, "2");
return name;
}
void read_init(read_prob_desc *rpd, Problem *p,
protect_in_read protect_which, uint Nsc, var_id sc_vars[],
uint Nr, var_id r_vars[], uint Nrs, var_id rs_vars[],
uint Nw1, var_id w1_vars[], uint Nw1s, var_id w1s_vars[],
uint Nw2, var_id w2_vars[], uint Nw2s, var_id w2s_vars[])
{
int v;
rpd->nonloops._first = 1;
rpd->nonloops._length = Nsc;
rpd->reads._first = 1 + Nsc;
rpd->reads._length = Nr;
rpd->rsteps._first = 1 + Nsc + Nr;
rpd->rsteps._length = Nrs;
rpd->write1s._first = 1 + Nsc + Nr + Nrs;
rpd->write1s._length = Nw1;
rpd->w1steps._first = 1 + Nsc + Nr + Nrs + Nw1;
rpd->w1steps._length = Nw1s;
rpd->write2s._first = 1 + Nsc + Nr + Nrs + Nw1 + Nw1s;
rpd->write2s._length = Nw2;
rpd->w2steps._first = 1 + Nsc + Nr + Nrs + Nw1 + Nw1s + Nw2;
rpd->w2steps._length = Nw2s;
if (read_Nvars(rpd) > maxVars) {
assert(0 && "Problem too big");
fprintf(stderr, "Too many variables for omega test\n");
Exit(2);
/* Could we handle this by not doing r/k/c? */
}
rpd->vars[0] = 0;
/* sc_vars[0..Nsc-1] are valid */
for (v = 0; v < Nsc; v++) {
assert(sc_vars[v] != NIL);
rpd->vars[v + r_first(&rpd->nonloops)] = sc_vars[v];
var_ids_tag(sc_vars[v]) = v + r_first(&rpd->nonloops);
}
/* r_vars[1..Nr] are valid */
for (v = 0; v < r_length(&rpd->reads); v++) {
assert(r_vars[v + 1] != NIL);
rpd->vars[v + r_first(&rpd->reads)] = r_vars[v + 1];
}
for (v = 0; v < r_length(&rpd->write1s); v++) {
assert(w1_vars[v + 1] != NIL);
rpd->vars[v + r_first(&rpd->write1s)] = w1_vars[v + 1];
}
for (v = 0; v < r_length(&rpd->write2s); v++) {
assert(w2_vars[v + 1] != NIL);
rpd->vars[v + r_first(&rpd->write2s)] = w2_vars[v + 1];
}
/* rs_vars[0..Nrs] hold steps FROM INNERMOST TO OUTERMOST LOOPS */
for (v = 0; v < Nrs; v++) {
assert(rs_vars[Nrs - 1 - v] == NIL);
rpd->vars[v + r_first(&rpd->rsteps)] = rs_vars[Nrs - 1 - v];
}
for (v = 0; v < Nw1s; v++) {
assert(w1s_vars[Nw1s - 1 - v] == NIL);
rpd->vars[v + r_first(&rpd->w1steps)] = w1s_vars[Nw1s - 1 - v];
}
for (v = 0; v < Nw2s; v++) {
assert(w2s_vars[Nw2s - 1 - v] == NIL);
rpd->vars[v + r_first(&rpd->w2steps)] = w2s_vars[Nw2s - 1 - v];
}
init_prob(p, read_Nvars(rpd),
protect_which == sc_and_r ? Nsc + Nr + Nrs : Nsc + Nr + Nrs + Nw1 + Nw1s,
read_getVarName, rpd);
}
/* clear the tags for symbolic constants */
void read_cleanup(read_prob_desc *rpd)
{
int v;
for (v = 0; v < r_length(&rpd->nonloops); v++) {
var_ids_tag(rpd->vars[v + r_first(&rpd->nonloops)]) = UNTAGGED;
}
}