Repository navigation
Codegen Patterns O2
Optimization makes control-flow shape and source ordering visible in less intuitive ways. A small rewrite can affect register pressure and code layout well before the statement that changed.
Symptom: The executable pushes all callee-saved registers in the prologue, while a candidate delays some pushes until after early-return checks. Likely source: the original funnels failures through a shared label and exit rather than using separate returns.
if (!first) goto fail;
if (!second) goto fail;
return 1;
fail:
return 0;Symptom: Loop-head alignment nops or register choices differ, and the candidate uses early continue statements. Likely source: the original wraps work in nested if/else blocks, possibly with an empty branch, changing the control-flow graph and cleanup joins.
for (auto item : items) {
if (item.matches()) {
process(item);
} else {
recordMiss(item);
}
}Symptom: The executable uses inc [member] inside the loop, while the candidate keeps the counter in a register and stores it afterward. Tail instructions may also appear in another order. Likely source: source statement ordering after the loop can change scheduling and register allocation.
while (more()) { ++count; }
kind = nextKind;
++column;
++length;Try plausible statement orders while preserving behavior.
Symptom: cmp index,bound; jae range_check; ... mov byte [buffer+index],0 appears before an indexed byte store. Likely source: a plain indexed write without an explicit bounds guard; compiler security checks add the range check.
buffer[length] = '\0';A branchy clamp may also result when an unsigned size is widened into another integer type through an explicit if/else assignment:
unsigned size = source.size();
std::size_t length;
if (size < limit) length = size;
else length = limit - 1;Symptom: The executable writes an immediate zero word while a candidate writes a zero register. Likely source: two adjacent byte assignments; the compiler can merge them late into a word store.
bytes[offset] = 0;
bytes[offset + 1] = 0;Symptom: The executable reloads a member pointer after each store or call, while a candidate caches it in a register. Likely source: the original expression reads the field anew at each use, perhaps through a helper or lambda.
auto current = [&] { return object->member; };
current()->write();
current()->finish();