When trying to negate a value, V, we try to avoid materializing the negated value by scanning the use list of V to see if we already have one. If we do find a negated version and that version is in the block we're currently optimizing, moving that instruction may invalidate our iterator.
Example:
define void @fn1(i32 %a, i1 %c, i32* %ptr) { entry: br label %for.cond for.cond: %d.0 = phi i32 [ 1, %entry ], [ 2, %for.body ] br i1 %c, label %for.end, label %for.body for.body: %sub1 = sub i32 %a, %d.0 %dead1 = add i32 %sub1, 1 %dead2 = mul i32 %dead1, 3 %dead3 = mul i32 %dead2, %sub1 %sub2 = sub nsw i32 0, %d.0 store i32 %sub2, i32* %ptr, align 4 br label %for.cond for.end: ret void }
The invalidation occurs as follows:
- Assume we're optimizing %dead3, which is trivially dead.
- The BB iterator is incremented (now pointing to %sub2) and EraseInst is called to delete %dead3.
- When the %dead3 instruction is removed its operand %sub1 is added to the RedoInst list.
- When %sub1 is reoptimized the ShouldBreakUpSubtract() function returns true and the BreakUpSubtract() function is called. This didn't happen the first time %sub1 was optimized because it had more than one use (i.e., %dead1 and %dead3).
- The NegateValue() function then hoists the %sub2 (as it is the negated form of %d.0) instruction into the for.cond block invalidating the iterator and causing the assertion. Without the assert we would dereference an invalid iterator and segfault.
Please take a look,
Chad