mirror of
https://github.com/golang/go.git
synced 2025-05-28 19:02:22 +00:00
[dev.ssa] cmd/compile/internal/ssa: fix defer in functions with no return
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>
This commit is contained in:
parent
8d081679d5
commit
ca9e450bed
@ -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 {
|
||||
|
@ -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") }
|
||||
|
21
src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go
vendored
Normal file
21
src/cmd/compile/internal/gc/testdata/deferNoReturn_ssa.go
vendored
Normal file
@ -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()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user