/// Called after code is generated, this function loops over all the ops
/// and figures out the lifetimes of all variables, based on whether the
/// args in each op are read or written.
void
OSLCompilerImpl::track_variable_lifetimes (const OpcodeVec &code,
                                           const SymbolPtrVec &opargs,
                                           const SymbolPtrVec &allsyms)
{
    // Clear the lifetimes for all symbols
    BOOST_FOREACH (Symbol *s, allsyms)
        s->clear_rw ();

    static ustring op_for("for");
    static ustring op_while("while");
    static ustring op_dowhile("dowhile");

    // For each op, mark its arguments as being used at that op
    int opnum = 0;
    BOOST_FOREACH (const Opcode &op, code) {
        // Some work to do for each argument to the op...
        for (int a = 0;  a < op.nargs();  ++a) {
            SymbolPtr s = opargs[op.firstarg()+a];
            ASSERT (s->dealias() == s);
            // s = s->dealias();   // Make sure it's de-aliased

            // Mark that it's read and/or written for this op
            s->mark_rw (opnum, op.argread(a), op.argwrite(a));
        }

        // If this is a loop op, we need to mark its control variable
        // (the only arg) as used for the duration of the loop!
        if (op.opname() == op_for ||
            op.opname() == op_while ||
            op.opname() == op_dowhile) {
            ASSERT (op.nargs() == 1);  // loops should have just one arg
            SymbolPtr s = opargs[op.firstarg()];
            s->mark_rw (opnum+1, true, true);
            s->mark_rw (op.farthest_jump()-1, true, true);
        }

        ++opnum;
    }


    // Special cases: handle variables whose lifetimes cross the boundaries
    // of a loop.
    opnum = 0;
    BOOST_FOREACH (const Opcode &op, code) {
        if (op.opname() == op_for ||
            op.opname() == op_while ||
            op.opname() == op_dowhile) {
            int loopcond = op.jump (0);  // after initialization, before test
            int loopend = op.farthest_jump() - 1;
            BOOST_FOREACH (Symbol *s, allsyms) {
                // Temporaries referenced both inside AND outside a loop
                // need their lifetimes extended to cover the entire
                // loop so they aren't coalesced incorrectly.  The
                // specific danger is for a function that contains a
                // loop, and the function is passed an argument that is
                // a temporary calculation.
                if (s->symtype() == SymTypeTemp &&
                    ((s->firstuse() < loopcond && s->lastuse() >= loopcond) ||
                     (s->firstuse() < loopend && s->lastuse() >= loopend))) {
                    s->mark_rw (opnum, true, true);
                    s->mark_rw (loopend, true, true);
                }

                // Locals that are written within the loop should have
                // their usage conservatively expanded to the whole
                // loop.  This is not a worry for temps, because they
                // CAN'T be read in the next iteration unless they were
                // set before the loop, handled above.  Ideally, we
                // could be less conservative if we knew that the
                // variable in question was declared/scoped internal to
                // the loop, in which case it can't carry values to the
                // next iteration (FIXME).
                if (s->symtype() == SymTypeLocal &&
                      s->firstuse() < loopend && s->lastwrite() >= loopcond) {
                    bool read = (s->lastread() >= loopcond);
                    s->mark_rw (opnum, read, true);
                    s->mark_rw (loopend, read, true);
                }
            }
        }
        ++opnum;
    }
/// Called after code is generated, this function loops over all the ops
/// and figures out the lifetimes of all variables, based on whether the
/// args in each op are read or written.
void
OSLCompilerImpl::track_variable_lifetimes (const OpcodeVec &code,
                                           const SymbolPtrVec &opargs,
                                           const SymbolPtrVec &allsyms)
{
    // Clear the lifetimes for all symbols
    BOOST_FOREACH (Symbol *s, allsyms)
        s->clear_rw ();

    static ustring op_for("for");
    static ustring op_while("while");
    static ustring op_dowhile("dowhile");

    // For each op, mark its arguments as being used at that op
    int opnum = 0;
    BOOST_FOREACH (const Opcode &op, code) {
        // Some work to do for each argument to the op...
        for (int a = 0;  a < op.nargs();  ++a) {
            SymbolPtr s = opargs[op.firstarg()+a];
            ASSERT (s->dealias() == s);
            // s = s->dealias();   // Make sure it's de-aliased

            // Mark that it's read and/or written for this op
            s->mark_rw (opnum, op.argread(a), op.argwrite(a));
        }

        // If this is a loop op, we need to mark its control variable
        // (the only arg) as used for the duration of the loop!
        if (op.opname() == op_for ||
            op.opname() == op_while ||
            op.opname() == op_dowhile) {
            ASSERT (op.nargs() == 1);  // loops should have just one arg
            SymbolPtr s = opargs[op.firstarg()];
            s->mark_rw (opnum+1, true, true);
            s->mark_rw (op.farthest_jump()-1, true, true);
        }

        ++opnum;
    }


    // Special case: temporaries referenced both inside AND outside a
    // loop need their lifetimes extended to cover the entire loop so
    // they aren't accidentally coalesced incorrectly.  The specific
    // danger is for a function that contains a loop, and the function
    // is passed an argument that is a temporary calculation.
    opnum = 0;
    BOOST_FOREACH (const Opcode &op, code) {
        if (op.opname() == op_for ||
            op.opname() == op_while ||
            op.opname() == op_dowhile) {
            int loopend = op.farthest_jump() - 1;
            BOOST_FOREACH (Symbol *s, allsyms) {
                if (s->symtype() == SymTypeTemp && 
                    ((s->firstuse() < opnum && s->lastuse() >= opnum) ||
                     (s->firstuse() < loopend && s->lastuse() >= loopend))) {
                    s->mark_rw (opnum, true, true);
                    s->mark_rw (loopend, true, true);
                }
            }
        }
        ++opnum;
    }
/// Called after code is generated, this function loops over all the ops
/// and figures out the lifetimes of all variables, based on whether the
/// args in each op are read or written.
void
OSLCompilerImpl::track_variable_lifetimes (const OpcodeVec &code,
                                           const SymbolPtrVec &opargs,
                                           const SymbolPtrVec &allsyms,
                                           std::vector<int> *bblockids)
{
    // Clear the lifetimes for all symbols
    BOOST_FOREACH (Symbol *s, allsyms)
        s->clear_rw ();

    // Keep track of the nested loops we're inside. We track them by pairs
    // of begin/end instruction numbers for that loop body, including
    // conditional evaluation (skip the initialization). Note that the end
    // is inclusive. We use this vector of ranges as a stack.
    typedef std::pair<int,int> intpair;
    std::vector<intpair> loop_bounds;

    // For each op, mark its arguments as being used at that op
    int opnum = 0;
    BOOST_FOREACH (const Opcode &op, code) {
        if (op.opname() == op_for || op.opname() == op_while ||
                op.opname() == op_dowhile) {
            // If this is a loop op, we need to mark its control variable
            // (the only arg) as used for the duration of the loop!
            ASSERT (op.nargs() == 1);  // loops should have just one arg
            SymbolPtr s = opargs[op.firstarg()];
            int loopcond = op.jump (0); // after initialization, before test
            int loopend = op.farthest_jump() - 1;   // inclusive end
            s->mark_rw (opnum+1, true, true);
            s->mark_rw (loopend, true, true);
            // Also push the loop bounds for this loop
            loop_bounds.push_back (std::make_pair(loopcond, loopend));
        }

        // Some work to do for each argument to the op...
        for (int a = 0;  a < op.nargs();  ++a) {
            SymbolPtr s = opargs[op.firstarg()+a];
            ASSERT (s->dealias() == s);  // Make sure it's de-aliased

            // Mark that it's read and/or written for this op
            bool readhere = op.argread(a);
            bool writtenhere = op.argwrite(a);
            s->mark_rw (opnum, readhere, writtenhere);

            // Adjust lifetimes of symbols whose values need to be preserved
            // between loop iterations.
            BOOST_FOREACH (intpair oprange, loop_bounds) {
                int loopcond = oprange.first;
                int loopend = oprange.second;
                DASSERT (s->firstuse() <= loopend);
                // Special case: a temp or local, even if written inside a
                // loop, if it's entire lifetime is within one basic block
                // and it's strictly written before being read, then its
                // lifetime is truly local and doesn't need to be expanded
                // for the duration of the loop.
                if (bblockids &&
                    (s->symtype()==SymTypeLocal || s->symtype()==SymTypeTemp) &&
                    (*bblockids)[s->firstuse()] == (*bblockids)[s->lastuse()] &&
                    s->lastwrite() < s->firstread()) {
                    continue;
                }
                // Syms written before or inside the loop, and referenced
                // inside or after the loop, need to preserve their value
                // for the duration of the loop. We know it's referenced
                // inside the loop because we're here examining it!
                if (s->firstwrite() <= loopend) {
                    s->mark_rw (loopcond, readhere, writtenhere);
                    s->mark_rw (loopend, readhere, writtenhere);
                }
            }
        }

        ++opnum;  // Advance to the next op index

        // Pop any loop bounds for loops we've just exited
        while (!loop_bounds.empty() && loop_bounds.back().second < opnum)
            loop_bounds.pop_back ();
    }