]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/compile/internal/ssa: fix defer in functions with no return
authorKeith Randall <khr@golang.org>
Tue, 8 Sep 2015 15:59:57 +0000 (08:59 -0700)
committerKeith Randall <khr@golang.org>
Tue, 8 Sep 2015 17:57:10 +0000 (17:57 +0000)
The after-defer test jumps to a deferreturn site.  Some functions
(those with infinite loops) have no deferreturn site.  Add one
so we have one to jump to.

Change-Id: I505e7f3f888f5e7d03ca49a3477b41cf1f78eb8a
Reviewed-on: https://go-review.googlesource.com/14349
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/ssa_test.go
src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go [new file with mode: 0644]

index 098a1e15f6835e09ad42b7b01a0f88e42b181c8a..70990bbd183539104d9fe9a4502050be91e62191 100644 (file)
@@ -2621,6 +2621,12 @@ func genssa(f *ssa.Func, ptxt *obj.Prog, gcargs, gclocals *Sym) {
        for _, br := range s.branches {
                br.p.To.Val = s.bstart[br.b.ID]
        }
+       if s.deferBranches != nil && s.deferTarget == nil {
+               // This can happen when the function has a defer but
+               // no return (because it has an infinite loop).
+               s.deferReturn()
+               Prog(obj.ARET)
+       }
        for _, p := range s.deferBranches {
                p.To.Val = s.deferTarget
        }
@@ -3463,20 +3469,7 @@ func (s *genState) genBlock(b, next *ssa.Block) {
        case ssa.BlockExit:
        case ssa.BlockRet:
                if Hasdefer != 0 {
-                       // Deferred calls will appear to be returning to
-                       // the CALL deferreturn(SB) that we are about to emit.
-                       // However, the stack trace code will show the line
-                       // of the instruction byte before the return PC.
-                       // To avoid that being an unrelated instruction,
-                       // insert an actual hardware NOP that will have the right line number.
-                       // This is different from obj.ANOP, which is a virtual no-op
-                       // that doesn't make it into the instruction stream.
-                       s.deferTarget = Pc
-                       Thearch.Ginsnop()
-                       p := Prog(obj.ACALL)
-                       p.To.Type = obj.TYPE_MEM
-                       p.To.Name = obj.NAME_EXTERN
-                       p.To.Sym = Linksym(Deferreturn.Sym)
+                       s.deferReturn()
                }
                Prog(obj.ARET)
        case ssa.BlockCall:
@@ -3537,6 +3530,23 @@ func (s *genState) genBlock(b, next *ssa.Block) {
        }
 }
 
+func (s *genState) deferReturn() {
+       // Deferred calls will appear to be returning to
+       // the CALL deferreturn(SB) that we are about to emit.
+       // However, the stack trace code will show the line
+       // of the instruction byte before the return PC.
+       // To avoid that being an unrelated instruction,
+       // insert an actual hardware NOP that will have the right line number.
+       // This is different from obj.ANOP, which is a virtual no-op
+       // that doesn't make it into the instruction stream.
+       s.deferTarget = Pc
+       Thearch.Ginsnop()
+       p := Prog(obj.ACALL)
+       p.To.Type = obj.TYPE_MEM
+       p.To.Name = obj.NAME_EXTERN
+       p.To.Sym = Linksym(Deferreturn.Sym)
+}
+
 // addAux adds the offset in the aux fields (AuxInt and Aux) of v to a.
 func addAux(a *obj.Addr, v *ssa.Value) {
        if a.Type != obj.TYPE_MEM {
index e4f5bcd1fdfdf74003a8050834d9aa28405587c0..0bce902982a9c11ebb366460828036b54fd31d47 100644 (file)
@@ -17,12 +17,18 @@ import (
 // TODO: move all these tests elsewhere?
 // Perhaps teach test/run.go how to run them with a new action verb.
 func runTest(t *testing.T, filename string) {
+       doTest(t, filename, "run")
+}
+func buildTest(t *testing.T, filename string) {
+       doTest(t, filename, "build")
+}
+func doTest(t *testing.T, filename string, kind string) {
        if runtime.GOARCH != "amd64" {
                t.Skipf("skipping SSA tests on %s for now", runtime.GOARCH)
        }
        testenv.MustHaveGoBuild(t)
        var stdout, stderr bytes.Buffer
-       cmd := exec.Command("go", "run", filepath.Join("testdata", filename))
+       cmd := exec.Command("go", kind, filepath.Join("testdata", filename))
        cmd.Stdout = &stdout
        cmd.Stderr = &stderr
        // TODO: set GOGC=off until we have stackmaps
@@ -70,3 +76,5 @@ func TestMap(t *testing.T) { runTest(t, "map_ssa.go") }
 func TestRegalloc(t *testing.T) { runTest(t, "regalloc_ssa.go") }
 
 func TestString(t *testing.T) { runTest(t, "string_ssa.go") }
+
+func TestDeferNoReturn(t *testing.T) { buildTest(t, "deferNoReturn_ssa.go") }
diff --git a/src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go b/src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go
new file mode 100644 (file)
index 0000000..171f583
--- /dev/null
@@ -0,0 +1,21 @@
+// compile
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a defer in a function with no return
+// statement will compile correctly.
+
+package main
+
+func deferNoReturn_ssa() {
+       defer func() { println("returned") }()
+       for {
+               println("loop")
+       }
+}
+
+func main() {
+       deferNoReturn_ssa()
+}