Commit ad351260 authored by James Willis's avatar James Willis
Browse files

Merge branch 'master' into dopair2-vectorisation

parents 631dbc0c 637853ce
......@@ -602,10 +602,10 @@ if test "$enable_warn" != "no"; then
# We will do this by hand instead and only default to the macro for unknown compilers
case "$ax_cv_c_compiler_vendor" in
gnu | clang)
CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter"
CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter -Wshadow"
;;
intel)
CFLAGS="$CFLAGS -w2 -Wunused-variable"
CFLAGS="$CFLAGS -w2 -Wunused-variable -Wshadow"
;;
*)
AX_CFLAGS_WARN_ALL
......
......@@ -153,12 +153,6 @@ int main(int argc, char *argv[]) {
#endif
/* Let's pin the main thread */
#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE)
if (((ENGINE_POLICY)&engine_policy_setaffinity) == engine_policy_setaffinity)
engine_pin();
#endif
/* Welcome to SWIFT, you made the right choice */
if (myrank == 0) greetings();
......@@ -329,6 +323,12 @@ int main(int argc, char *argv[]) {
return 1;
}
/* Let's pin the main thread, now we know if affinity will be used. */
#if defined(HAVE_SETAFFINITY) && defined(HAVE_LIBNUMA) && defined(_GNU_SOURCE)
if (with_aff && ((ENGINE_POLICY)&engine_policy_setaffinity) == engine_policy_setaffinity)
engine_pin();
#endif
/* Genesis 1.1: And then, there was time ! */
clocks_set_cpufreq(cpufreq);
......
......@@ -1859,10 +1859,12 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
struct task *t = l->t;
struct cell *ci = t->ci;
struct cell *cj = t->cj;
const int ci_active = cell_is_active(ci, e);
const int cj_active = (cj != NULL) ? cell_is_active(cj, e) : 0;
/* Only activate tasks that involve a local active cell. */
if ((cell_is_active(ci, e) && ci->nodeID == engine_rank) ||
(cj != NULL && cell_is_active(cj, e) && cj->nodeID == engine_rank)) {
if ((ci_active && ci->nodeID == engine_rank) ||
(cj_active && cj->nodeID == engine_rank)) {
scheduler_activate(s, t);
/* Activate hydro drift */
......@@ -1904,9 +1906,9 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
if (ci->nodeID != engine_rank) {
/* If the local cell is active, receive data from the foreign cell. */
if (cell_is_active(cj, e)) {
if (cj_active) {
scheduler_activate(s, ci->recv_xv);
if (cell_is_active(ci, e)) {
if (ci_active) {
scheduler_activate(s, ci->recv_rho);
#ifdef EXTRA_HYDRO_LOOP
scheduler_activate(s, ci->recv_gradient);
......@@ -1915,55 +1917,36 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
}
/* If the foreign cell is active, we want its ti_end values. */
if (cell_is_active(ci, e)) scheduler_activate(s, ci->recv_ti);
if (ci_active) scheduler_activate(s, ci->recv_ti);
/* Is the foreign cell active and will need stuff from us? */
if (ci_active) {
/* Look for the local cell cj's send tasks. */
if (cell_is_active(ci, e)) {
struct link *l = NULL;
for (l = cj->send_xv; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_xv task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, cj->send_xv, ci->nodeID);
/* Drift the cell which will be sent; note that not all sent
particles will be drifted, only those that are needed. */
cell_activate_drift_part(cj, s);
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = cj->send_rho; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
/* If the local cell is also active, more stuff will be needed. */
if (cj_active) {
scheduler_activate_send(s, cj->send_rho, ci->nodeID);
#ifdef EXTRA_HYDRO_LOOP
for (l = cj->send_gradient;
l != NULL && l->t->cj->nodeID != ci->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_gradient task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, cj->send_gradient, ci->nodeID);
#endif
}
}
/* If the local cell is active, send its ti_end values. */
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
if (cj_active) scheduler_activate_send(s, cj->send_ti, ci->nodeID);
} else if (cj->nodeID != engine_rank) {
/* If the local cell is active, receive data from the foreign cell. */
if (cell_is_active(ci, e)) {
if (ci_active) {
scheduler_activate(s, cj->recv_xv);
if (cell_is_active(cj, e)) {
if (cj_active) {
scheduler_activate(s, cj->recv_rho);
#ifdef EXTRA_HYDRO_LOOP
scheduler_activate(s, cj->recv_gradient);
......@@ -1972,49 +1955,30 @@ int cell_unskip_tasks(struct cell *c, struct scheduler *s) {
}
/* If the foreign cell is active, we want its ti_end values. */
if (cell_is_active(cj, e)) scheduler_activate(s, cj->recv_ti);
if (cj_active) scheduler_activate(s, cj->recv_ti);
/* Is the foreign cell active and will need stuff from us? */
if (cj_active) {
/* Look for the local cell ci's send tasks. */
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_xv task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, ci->send_xv, cj->nodeID);
/* Drift the cell which will be sent; note that not all sent
particles will be drifted, only those that are needed. */
cell_activate_drift_part(ci, s);
if (cell_is_active(ci, e)) {
/* If the local cell is also active, more stuff will be needed. */
if (ci_active) {
struct link *l = NULL;
for (l = ci->send_rho; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, ci->send_rho, cj->nodeID);
#ifdef EXTRA_HYDRO_LOOP
for (l = ci->send_gradient;
l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_gradient task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, ci->send_gradient, cj->nodeID);
#endif
}
}
/* If the local cell is active, send its ti_end values. */
if (cell_is_active(ci, e)) {
struct link *l = NULL;
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
if (ci_active) scheduler_activate_send(s, ci->send_ti, cj->nodeID);
}
#endif
}
......
......@@ -708,11 +708,11 @@ void engine_redistribute(struct engine *e) {
size_t total = 0, g_total = 0, s_total = 0;
size_t unmoved = 0, g_unmoved = 0, s_unmoved = 0;
for (int p = 0, r = 0; p < nr_nodes; p++) {
for (int s = 0; s < nr_nodes; s++) {
for (int n = 0; n < nr_nodes; n++) {
total += counts[r];
g_total += g_counts[r];
s_total += s_counts[r];
if (p == s) {
if (p == n) {
unmoved += counts[r];
g_unmoved += g_counts[r];
s_unmoved += s_counts[r];
......@@ -2690,10 +2690,12 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
/* Local pointers. */
struct cell *ci = t->ci;
struct cell *cj = t->cj;
const int ci_active = cell_is_active(ci, e);
const int cj_active = cell_is_active(cj, e);
/* Only activate tasks that involve a local active cell. */
if ((cell_is_active(ci, e) && ci->nodeID == engine_rank) ||
(cell_is_active(cj, e) && cj->nodeID == engine_rank)) {
if ((ci_active && ci->nodeID == engine_rank) ||
(cj_active && cj->nodeID == engine_rank)) {
scheduler_activate(s, t);
/* Set the correct sorting flags */
......@@ -2741,9 +2743,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
if (ci->nodeID != engine_rank) {
/* If the local cell is active, receive data from the foreign cell. */
if (cell_is_active(cj, e)) {
if (cj_active) {
scheduler_activate(s, ci->recv_xv);
if (cell_is_active(ci, e)) {
if (ci_active) {
scheduler_activate(s, ci->recv_rho);
#ifdef EXTRA_HYDRO_LOOP
scheduler_activate(s, ci->recv_gradient);
......@@ -2752,56 +2754,38 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
}
/* If the foreign cell is active, we want its ti_end values. */
if (cell_is_active(ci, e)) scheduler_activate(s, ci->recv_ti);
if (ci_active) scheduler_activate(s, ci->recv_ti);
/* Look for the local cell cj's send tasks. */
if (cell_is_active(ci, e)) {
struct link *l = NULL;
for (l = cj->send_xv; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_xv task.");
scheduler_activate(s, l->t);
/* Is the foreign cell active and will need stuff from us? */
if (ci_active) {
struct link *l =
scheduler_activate_send(s, cj->send_xv, ci->nodeID);
/* Drift the cell which will be sent at the level at which it is
sent, i.e. drift the cell specified in the send task (l->t)
itself. */
cell_activate_drift_part(l->t->ci, s);
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = cj->send_rho;
l != NULL && l->t->cj->nodeID != ci->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
/* If the local cell is also active, more stuff will be needed. */
if (cj_active) {
scheduler_activate_send(s, cj->send_rho, ci->nodeID);
#ifdef EXTRA_HYDRO_LOOP
for (l = cj->send_gradient;
l != NULL && l->t->cj->nodeID != ci->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_gradient task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, cj->send_gradient, ci->nodeID);
#endif
}
}
/* If the local cell is active, send its ti_end values. */
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = cj->send_ti; l != NULL && l->t->cj->nodeID != ci->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
if (cj_active) scheduler_activate_send(s, cj->send_ti, ci->nodeID);
} else if (cj->nodeID != engine_rank) {
/* If the local cell is active, receive data from the foreign cell. */
if (cell_is_active(ci, e)) {
if (ci_active) {
scheduler_activate(s, cj->recv_xv);
if (cell_is_active(cj, e)) {
if (cj_active) {
scheduler_activate(s, cj->recv_rho);
#ifdef EXTRA_HYDRO_LOOP
scheduler_activate(s, cj->recv_gradient);
......@@ -2810,50 +2794,32 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
}
/* If the foreign cell is active, we want its ti_end values. */
if (cell_is_active(cj, e)) scheduler_activate(s, cj->recv_ti);
if (cj_active) scheduler_activate(s, cj->recv_ti);
/* Look for the local cell ci's send tasks. */
if (cell_is_active(cj, e)) {
struct link *l = NULL;
for (l = ci->send_xv; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_xv task.");
scheduler_activate(s, l->t);
/* Is the foreign cell active and will need stuff from us? */
if (cj_active) {
struct link *l =
scheduler_activate_send(s, ci->send_xv, cj->nodeID);
/* Drift the cell which will be sent at the level at which it is
sent, i.e. drift the cell specified in the send task (l->t)
itself. */
cell_activate_drift_part(l->t->ci, s);
if (cell_is_active(ci, e)) {
/* If the local cell is also active, more stuff will be needed. */
if (ci_active) {
struct link *l = NULL;
for (l = ci->send_rho;
l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_rho task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, ci->send_rho, cj->nodeID);
#ifdef EXTRA_HYDRO_LOOP
for (l = ci->send_gradient;
l != NULL && l->t->cj->nodeID != cj->nodeID; l = l->next)
;
if (l == NULL) error("Missing link to send_gradient task.");
scheduler_activate(s, l->t);
scheduler_activate_send(s, ci->send_gradient, cj->nodeID);
#endif
}
}
/* If the local cell is active, send its ti_end values. */
if (cell_is_active(ci, e)) {
struct link *l = NULL;
for (l = ci->send_ti; l != NULL && l->t->cj->nodeID != cj->nodeID;
l = l->next)
;
if (l == NULL) error("Missing link to send_ti task.");
scheduler_activate(s, l->t);
}
if (ci_active) scheduler_activate_send(s, ci->send_ti, cj->nodeID);
}
#endif
}
......@@ -4502,8 +4468,9 @@ void engine_init(struct engine *e, struct space *s,
/* Avoid (unexpected) interference between engine and runner threads. We can
* do this once we've made at least one call to engine_entry_affinity and
* maybe numa_node_of_cpu(sched_getcpu()), even if the engine isn't already
* pinned. Also unpin this when asked to not pin at all (!with_aff). */
engine_unpin();
* pinned. */
if (with_aff)
engine_unpin();
#endif
if (with_aff) {
......
......@@ -95,7 +95,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_grav_pp_truncated(
} else {
const float r = r2 * r_inv;
const float ui = r * h_inv;
float W_ij;
......
......@@ -24,6 +24,7 @@
* @brief Empty SPH implementation used solely to test the SELF/PAIR routines.
*/
#include "equation_of_state.h"
#include "hydro_properties.h"
#include "hydro_space.h"
#include "part.h"
......
......@@ -345,4 +345,3 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
runner_iact_fluxes_common(r2, dx, hi, hj, pi, pj, 0);
}
......@@ -1612,83 +1612,87 @@ __attribute__((always_inline)) INLINE void voronoi_intersect(
continue;
}
if (c->orders[v] == 2) {
int j = voronoi_get_edge(c, v, 0);
int k = voronoi_get_edge(c, v, 1);
int b = voronoi_get_edgeindex(c, v, 1);
int l = 0;
safewhile(l < c->orders[j] && voronoi_get_edge(c, j, l) != k) { ++l; }
if (l == c->orders[j]) {
int jj = voronoi_get_edge(c, v, 0);
int kk = voronoi_get_edge(c, v, 1);
int bb = voronoi_get_edgeindex(c, v, 1);
int ll = 0;
safewhile(ll < c->orders[jj] && voronoi_get_edge(c, jj, ll) != kk) {
++ll;
}
if (ll == c->orders[jj]) {
int a = voronoi_get_edgeindex(c, v, 0);
/* j and k are not joined together. Replace their edges pointing to v
with a new edge pointing from j to k */
voronoi_set_edge(c, j, a, k);
voronoi_set_edgeindex(c, j, a, b);
voronoi_set_edge(c, k, b, j);
voronoi_set_edgeindex(c, k, b, a);
/* jj and kk are not joined together. Replace their edges pointing to v
with a new edge pointing from jj to kk */
voronoi_set_edge(c, jj, a, k);
voronoi_set_edgeindex(c, jj, a, bb);
voronoi_set_edge(c, kk, bb, jj);
voronoi_set_edgeindex(c, kk, bb, a);
/* no new elements added to the stack: decrease the counter */
--low_order_index;
} else {
/* just remove the edges from j to v and from k to v: create two new
/* just remove the edges from jj to v and from kk to v: create two new
vertices */
/* vertex j */
/* vertex jj */
vindex = c->nvert;
++c->nvert;
c->vertices[3 * vindex] = c->vertices[3 * j];
c->vertices[3 * vindex + 1] = c->vertices[3 * j + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * j + 2];
c->orders[vindex] = c->orders[j] - 1;
c->vertices[3 * vindex] = c->vertices[3 * jj];
c->vertices[3 * vindex + 1] = c->vertices[3 * jj + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * jj + 2];
c->orders[vindex] = c->orders[jj] - 1;
c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1];
int m = 0;
for (int n = 0; n < c->orders[j]; ++n) {
int l = voronoi_get_edge(c, j, n);
if (l != v) {
for (int n = 0; n < c->orders[jj]; ++n) {
int lll = voronoi_get_edge(c, jj, n);
if (lll != v) {
/* make a new edge */
voronoi_set_edge(c, vindex, m, l);
voronoi_set_edgeindex(c, vindex, m, voronoi_get_edgeindex(c, j, n));
voronoi_set_edge(c, vindex, m, lll);
voronoi_set_edgeindex(c, vindex, m,
voronoi_get_edgeindex(c, jj, n));
/* update the other vertex */
voronoi_set_edge(c, l, voronoi_get_edgeindex(c, j, n), vindex);
voronoi_set_edgeindex(c, l, voronoi_get_edgeindex(c, j, n), m);
voronoi_set_edge(c, lll, voronoi_get_edgeindex(c, jj, n), vindex);
voronoi_set_edgeindex(c, lll, voronoi_get_edgeindex(c, jj, n), m);
/* copy ngb information */
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, j, n));
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, jj, n));
++m;
}
/* remove the old vertex */
voronoi_set_edge(c, j, n, -1);
voronoi_set_edgeindex(c, j, n, -1);
voronoi_set_edge(c, jj, n, -1);
voronoi_set_edgeindex(c, jj, n, -1);
}
/* vertex k */
/* vertex kk */
vindex = c->nvert;
++c->nvert;
c->vertices[3 * vindex] = c->vertices[3 * k];
c->vertices[3 * vindex + 1] = c->vertices[3 * k + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * k + 2];
c->orders[vindex] = c->orders[k] - 1;
c->vertices[3 * vindex] = c->vertices[3 * kk];
c->vertices[3 * vindex + 1] = c->vertices[3 * kk + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * kk + 2];
c->orders[vindex] = c->orders[kk] - 1;
c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1];
m = 0;
for (int n = 0; n < c->orders[k]; ++n) {
int l = voronoi_get_edge(c, k, n);
if (l != v) {
for (int n = 0; n < c->orders[kk]; ++n) {
int lll = voronoi_get_edge(c, kk, n);
if (lll != v) {
/* make a new edge */
voronoi_set_edge(c, vindex, m, l);
voronoi_set_edgeindex(c, vindex, m, voronoi_get_edgeindex(c, k, n));
voronoi_set_edge(c, vindex, m, lll);
voronoi_set_edgeindex(c, vindex, m,
voronoi_get_edgeindex(c, kk, n));
/* update the other vertex */
voronoi_set_edge(c, l, voronoi_get_edgeindex(c, k, n), vindex);
voronoi_set_edgeindex(c, l, voronoi_get_edgeindex(c, k, n), m);
voronoi_set_edge(c, lll, voronoi_get_edgeindex(c, kk, n), vindex);
voronoi_set_edgeindex(c, lll, voronoi_get_edgeindex(c, kk, n), m);
/* copy ngb information */
/* this one is special: we copy the ngb corresponding to the
deleted edge and skip the one after that */
if (n == b + 1) {
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, k, b));
if (n == bb + 1) {
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, kk, bb));
} else {
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, k, n));
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, kk, n));
}
++m;
}
/* remove the old vertex */
voronoi_set_edge(c, k, n, -1);
voronoi_set_edgeindex(c, k, n, -1);
voronoi_set_edge(c, kk, n, -1);
voronoi_set_edgeindex(c, kk, n, -1);
}
/* check if j or k has become an order 2 vertex */
/* check if jj or kk has become an order 2 vertex */
/* if they have become an order 1 vertex, they were already an order 2
vertex, and they should already be in the list... */
if (c->orders[vindex] == 2) {
......@@ -1716,32 +1720,32 @@ __attribute__((always_inline)) INLINE void voronoi_intersect(
voronoi_set_edge(c, v, 1, -1);
voronoi_set_edgeindex(c, v, 1, -1);
} else if (c->orders[v] == 1) {
int j = voronoi_get_edge(c, v, 0);
int jj = voronoi_get_edge(c, v, 0);
/* we have to remove the edge between j and v. We create a new vertex */
vindex = c->nvert;
++c->nvert;
c->vertices[3 * vindex] = c->vertices[3 * j];
c->vertices[3 * vindex + 1] = c->vertices[3 * j + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * j + 2];
c->vertices[3 * vindex] = c->vertices[3 * jj];
c->vertices[3 * vindex + 1] = c->vertices[3 * jj + 1];
c->vertices[3 * vindex + 2] = c->vertices[3 * jj + 2];
c->orders[vindex] = c->orders[j] - 1;
c->offsets[vindex] = c->offsets[vindex - 1] + c->orders[vindex - 1];
int m = 0;
for (int k = 0; k < c->orders[j]; ++k) {
int l = voronoi_get_edge(c, j, k);
if (l != v) {
for (int kk = 0; kk < c->orders[j]; ++kk) {
int ll = voronoi_get_edge(c, jj, kk);
if (ll != v) {
/* make a new edge */
voronoi_set_edge(c, vindex, m, l);
voronoi_set_edgeindex(c, vindex, m, voronoi_get_edgeindex(c, j, k));
voronoi_set_edge(c, vindex, m, ll);
voronoi_set_edgeindex(c, vindex, m, voronoi_get_edgeindex(c, jj, kk));
/* update the other vertex */
voronoi_set_edge(c, l, voronoi_get_edgeindex(c, j, k), vindex);
voronoi_set_edgeindex(c, l, voronoi_get_edgeindex(c, j, k), m);
voronoi_set_edge(c, ll, voronoi_get_edgeindex(c, jj, kk), vindex);
voronoi_set_edgeindex(c, ll, voronoi_get_edgeindex(c, jj, kk), m);
/* copy ngb information */
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, j, k));
voronoi_set_ngb(c, vindex, m, voronoi_get_ngb(c, jj, kk));
++m;
}
/* remove the old vertex */
voronoi_set_edge(c, j, k, -1);
voronoi_set_edgeindex(c, j, k, -1);
voronoi_set_edge(c, jj, kk, -1);
voronoi_set_edgeindex(c, jj, kk, -1);
}
/* if the new vertex is a new order 2 vertex, add it to the stack */
if (c->orders[vindex] == 2) {
......
......@@ -43,4 +43,32 @@