Skip to content

Commit 1d5b94b

Browse files
authored
Fix subtle bug in optimize_ops that could seg fault. (#651)
The optimize_ops method had a convenience reference to the op being examined in the loop. We carefully do a reserve() first to make sure that there was enough room for one insertion so that the reference would still be valid if the constant-folding function has to add an op. But this relied on the assumption that no folder would insert more than one op as it did its work, which was always true (once). Oops, much later, a new way to speed up "mix" ops had a particular corner case in which up to 7 new ops might be inserted, and I never went back and fixed the now-broken assumption here. The fix here is to change the reference to the op (which cannot be changed dynamically) into a pointer to the op, which can be re-pointed after calling the folding function to make sure that it's valid and pointing to the (potentially new) correct position as it continues on in the loop. So it is no longer sensitive to changes to the vector allocation that might happen inside the folder. I haven't changed this code for a long time. It's shocking that no problems cropped up from this bug until this week! I guess it's not a very common "corner case".
1 parent db265b3 commit 1d5b94b

1 file changed

Lines changed: 28 additions & 29 deletions

File tree

src/liboslexec/runtimeoptimize.cpp

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,20 +2044,17 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
20442044
for (int opnum = beginop; opnum < endop; opnum += 1) {
20452045
ASSERT (old_num_ops == num_ops); // better not happen unknowingly
20462046
DASSERT (num_ops == inst()->ops().size());
2047+
DASSERT (size_t(opnum) < inst()->ops().size());
20472048
if (m_stop_optimizing)
20482049
break;
2049-
// Before getting a reference to this op, be sure that a space
2050-
// is reserved at the end in case a folding routine inserts an
2051-
// op. That ensures that the reference won't be invalid.
2052-
inst()->ops().reserve (num_ops+1);
2053-
Opcode &op (inst()->ops()[opnum]);
2050+
Opcode *op = &inst()->ops()[opnum];
20542051
if (skipops) {
20552052
// If a previous optimization inserted ops and told us
20562053
// to skip over the new ones, we still need to unalias
20572054
// any symbols written by this op, but otherwise skip
20582055
// all subsequent optimizations until we run down the
20592056
// skipops counter.
2060-
block_unalias_written_args (op);
2057+
block_unalias_written_args (*op);
20612058
ASSERT (lastblock == m_bblockids[opnum] &&
20622059
"this should not be a new basic block");
20632060
--skipops;
@@ -2071,31 +2068,31 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
20712068
lastblock = m_bblockids[opnum];
20722069
}
20732070
// Nothing below here to do for no-ops, take early out.
2074-
if (op.opname() == u_nop)
2071+
if (op->opname() == u_nop)
20752072
continue;
20762073
// De-alias the readable args to the op and figure out if
20772074
// there are any constants involved.
2078-
for (int i = 0, e = op.nargs(); i < e; ++i) {
2079-
if (! op.argwrite(i)) { // Don't de-alias args that are written
2080-
int argindex = op.firstarg() + i;
2075+
for (int i = 0, e = op->nargs(); i < e; ++i) {
2076+
if (! op->argwrite(i)) { // Don't de-alias args that are written
2077+
int argindex = op->firstarg() + i;
20812078
int argsymindex = dealias_symbol (inst()->arg(argindex), opnum);
20822079
inst()->args()[argindex] = argsymindex;
20832080
}
2084-
if (op.argread(i))
2085-
use_stale_sym (oparg(op,i));
2081+
if (op->argread(i))
2082+
use_stale_sym (oparg(*op,i));
20862083
}
20872084
// If it's a simple assignment and the lvalue is "stale", go
20882085
// back and eliminate its last assignment.
2089-
if (is_simple_assign(op))
2090-
simple_sym_assign (oparg (op, 0), opnum);
2086+
if (is_simple_assign(*op))
2087+
simple_sym_assign (oparg (*op, 0), opnum);
20912088
// Make sure there's room for several more symbols, so that we
20922089
// can add a few consts if we need to, without worrying about
20932090
// the addresses of symbols changing when we add a new one below.
20942091
make_symbol_room (max_new_consts_per_fold);
20952092
// For various ops that we know how to effectively
20962093
// constant-fold, dispatch to the appropriate routine.
20972094
if (optimize() >= 2 && m_opt_constant_fold) {
2098-
const OpDescriptor *opd = shadingsys().op_descriptor (op.opname());
2095+
const OpDescriptor *opd = shadingsys().op_descriptor (op->opname());
20992096
if (opd && opd->folder) {
21002097
int c = (*opd->folder) (*this, opnum);
21012098
if (c) {
@@ -2105,23 +2102,24 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
21052102
skipops = num_ops - old_num_ops;
21062103
endop += num_ops - old_num_ops; // adjust how far we loop
21072104
old_num_ops = num_ops;
2105+
op = &inst()->ops()[opnum]; // in case ops resized
21082106
}
21092107
}
21102108
}
21112109
// Clear local block aliases for any args that were written
21122110
// by this op
2113-
block_unalias_written_args (op);
2111+
block_unalias_written_args (*op);
21142112

21152113
// Now we handle assignments.
2116-
if (optimize() >= 2 && op.opname() == u_assign && m_opt_assign)
2117-
changed += optimize_assignment (op, opnum);
2114+
if (optimize() >= 2 && op->opname() == u_assign && m_opt_assign)
2115+
changed += optimize_assignment (*op, opnum);
21182116
if (optimize() >= 2 && m_opt_elide_useless_ops)
2119-
changed += useless_op_elision (op, opnum);
2117+
changed += useless_op_elision (*op, opnum);
21202118
if (m_stop_optimizing)
21212119
break;
21222120
// Peephole optimization involving pair of instructions (the second
21232121
// instruction will be in the same basic block.
2124-
if (optimize() >= 2 && m_opt_peephole && op.opname() != u_nop) {
2122+
if (optimize() >= 2 && m_opt_peephole && op->opname() != u_nop) {
21252123
// Find the next instruction in the same basic block
21262124
int op2num = next_block_instruction (opnum);
21272125
if (op2num) {
@@ -2133,21 +2131,22 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
21332131
// skipops = num_ops - old_num_ops;
21342132
endop += num_ops - old_num_ops; // adjust how far we loop
21352133
old_num_ops = num_ops;
2134+
op = &inst()->ops()[opnum]; // in case ops resized
21362135
}
21372136
}
21382137
}
21392138

21402139
// Special cases for "if", "functioncall", and loops: Optimize the
21412140
// sequences of instructions in the bodies recursively in a way that
21422141
// allows us to be clever about the basic block alias tracking.
2143-
ustring opname = op.opname();
2142+
ustring opname = op->opname();
21442143
if ((opname == u_if || opname == u_functioncall ||
21452144
opname == u_for || opname == u_while || opname == u_dowhile)
21462145
&& shadingsys().m_opt_seed_bblock_aliases) {
21472146
// Find all symbols written anywhere in the instruction range
21482147
// of the bodies.
21492148
FastIntSet symwrites;
2150-
catalog_symbol_writes (opnum+1, op.farthest_jump(), symwrites);
2149+
catalog_symbol_writes (opnum+1, op->farthest_jump(), symwrites);
21512150
// Save the aliases from the basic block we are exiting.
21522151
// If & function call: save all prior aliases.
21532152
// Loops: dont save aliases involving syms written in the loop.
@@ -2163,11 +2162,11 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
21632162
// one (the body), loops have 4 (init, cond, body, incr),
21642163
int njumps = (opname == u_if) ? 2 : (opname == u_functioncall ? 1 : 4);
21652164
// Recursively optimize each body block.
2166-
// Don't use op after inserstions! Use inst()->op(opnum).
2167-
for (int j = 0; j < njumps; ++j)
2168-
changed += optimize_ops (j==0 ? opnum+1 : inst()->op(opnum).jump(j-1),
2169-
inst()->op(opnum).jump(j),
2170-
&saved_block_aliases);
2165+
for (int j = 0; j < njumps; ++j) {
2166+
changed += optimize_ops (j==0 ? opnum+1 : op->jump(j-1),
2167+
op->jump(j), &saved_block_aliases);
2168+
op = &inst()->ops()[opnum]; // in case ops resized
2169+
}
21712170
// Adjust optimization loop end if any instructions were added
21722171
num_ops = inst()->ops().size();
21732172
endop += num_ops - old_num_ops;
@@ -2182,13 +2181,13 @@ RuntimeOptimizer::optimize_ops (int beginop, int endop,
21822181
restored_aliases.swap (saved_block_aliases);
21832182
// catalog again, in case optimizations in those blocks
21842183
// caused writes that weren't apparent before.
2185-
catalog_symbol_writes (opnum+1, inst()->op(opnum).farthest_jump(), symwrites);
2184+
catalog_symbol_writes (opnum+1, op->farthest_jump(), symwrites);
21862185
copy_block_aliases (restored_aliases, saved_block_aliases,
21872186
&symwrites);
21882187
}
21892188
seed_block_aliases = &saved_block_aliases;
21902189
// Get ready to increment to the next instruction
2191-
opnum = inst()->op(opnum).farthest_jump() - 1;
2190+
opnum = op->farthest_jump() - 1;
21922191
}
21932192
}
21942193
m_block_aliases_stack.pop_back(); // Done with saved_block_aliases

0 commit comments

Comments
 (0)