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
}
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:
}
}
+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 {
// 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
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") }
--- /dev/null
+// 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()
+}