]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gc: correct liveness for various non-returning functions
authorRuss Cox <rsc@golang.org>
Fri, 14 Feb 2014 05:38:24 +0000 (00:38 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 14 Feb 2014 05:38:24 +0000 (00:38 -0500)
When the liveness code doesn't know a function doesn't return
(but the generated code understands that), the liveness analysis
invents a control flow edge that is not really there, which can cause
variables to seem spuriously live. This is particularly bad when the
variables are uninitialized.

TBR=iant
CC=golang-codereviews
https://golang.org/cl/63720043

src/cmd/gc/popt.c
test/live.go

index 7fcf5db10d57b1d7813f9b11ba147dddbfe664fa..1f0bdb496a6fe85812fbb1ff7aca877457ccc782 100644 (file)
@@ -51,6 +51,9 @@ noreturn(Prog *p)
                symlist[2] = pkglookup("throwinit", runtimepkg);
                symlist[3] = pkglookup("panic", runtimepkg);
                symlist[4] = pkglookup("panicwrap", runtimepkg);
+               symlist[5] = pkglookup("throwreturn", runtimepkg);
+               symlist[6] = pkglookup("selectgo", runtimepkg);
+               symlist[7] = pkglookup("block", runtimepkg);
        }
 
        if(p->to.node == nil)
index 032d39812d8e79c9ae069373a3916b6100cacde7..9c4e754c17b6100a7b9cf6458e98a277815547ed 100644 (file)
@@ -121,3 +121,64 @@ func f10() string {
        panic(1)
 }
 
+// liveness formerly confused by select, thinking runtime.selectgo
+// can return to next instruction; it always jumps elsewhere.
+// note that you have to use at least two cases in the select
+// to get a true select; smaller selects compile to optimized helper functions.
+
+var c chan *int
+var b bool
+
+// this used to have a spurious "live at entry to f11a: ~r0"
+func f11a() *int {
+       select { // ERROR "live at call to selectgo: autotmp"
+       case <-c: // ERROR "live at call to selectrecv: autotmp"
+               return nil
+       case <-c: // ERROR "live at call to selectrecv: autotmp"
+               return nil
+       }
+}
+
+func f11b() *int {
+       p := new(int)
+       if b {
+               // At this point p is dead: the code here cannot
+               // get to the bottom of the function.
+               // This used to have a spurious "live at call to printint: p".
+               print(1) // nothing live here!
+               select { // ERROR "live at call to selectgo: autotmp"
+               case <-c: // ERROR "live at call to selectrecv: autotmp"
+                       return nil
+               case <-c: // ERROR "live at call to selectrecv: autotmp"
+                       return nil
+               }
+       }
+       println(*p)
+       return nil
+}
+
+func f11c() *int {
+       p := new(int)
+       if b {
+               // Unlike previous, the cases in this select fall through,
+               // so we can get to the println, so p is not dead.
+               print(1) // ERROR "live at call to printint: p"
+               select { // ERROR "live at call to newselect: p" "live at call to selectgo: autotmp.* p"
+               case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
+               case <-c: // ERROR "live at call to selectrecv: autotmp.* p"
+               }
+       }
+       println(*p)
+       return nil
+}
+
+// similarly, select{} does not fall through.
+// this used to have a spurious "live at entry to f12: ~r0".
+
+func f12() *int {
+       if b {
+               select{}
+       } else {
+               return nil
+       }
+}