The registerization code needs the function to end in a RET,
even if that RET is actually unreachable.
The liveness code needs to avoid such unreachable RETs.
It had a special case for final RET after JMP, but no case
for final RET after UNDEF. Instead of expanding the special
cases, let fixjmp - which already knows what is and is not
reachable definitively - mark the unreachable RET so that
the liveness code can identify it.
TBR=iant
CC=golang-codereviews
https://golang.org/cl/
63680043
break;
bb->last = p;
- // Pattern match an unconditional branch followed by a
- // dead return instruction. This avoids a creating
+ // Stop before an unreachable RET, to avoid creating
// unreachable control flow nodes.
- if(p->link != nil && p->link->link == nil)
- if (p->as == AJMP && p->link->as == ARET && p->link->opt == nil)
- break;
+ if(p->link != nil && p->link->as == ARET && p->link->mode == -1)
+ break;
// Collect basic blocks with selectgo calls.
if(isselectgocall(p))
if(p->opt == dead) {
if(p->link == P && p->as == ARET && last && last->as != ARET) {
// This is the final ARET, and the code so far doesn't have one.
- // Let it stay.
+ // Let it stay. The register allocator assumes that all live code in
+ // the function can be traversed by starting at all the RET instructions
+ // and following predecessor links. If we remove the final RET,
+ // this assumption will not hold in the case of an infinite loop
+ // at the end of a function.
+ // Keep the RET but mark it dead for the liveness analysis.
+ p->mode = -1;
} else {
if(debug['R'] && debug['v'])
print("del %P\n", p);
x := i9
return x != 99
}
+
+// liveness formerly confused by UNDEF followed by RET,
+// leading to "live at entry to f10: ~r1" (unnamed result).
+
+func f10() string {
+ panic(1)
+}
+