diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 70049a89cc..eb027c6a67 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -84,6 +84,20 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == deferreturn) { + // Deferred calls will appear to be returning to + // the BL deferreturn(SB) that we are about to emit. + // However, the stack trace code will show the line + // of the instruction before that return PC. + // To avoid that instruction being an unrelated instruction, + // insert a NOP so that we will have the right line number. + // ARM NOP 0x00000000 is really AND.EQ R0, R0, R0. + // Use the latter form because the NOP pseudo-instruction + // would be removed by the linker. + nodreg(&r, types[TINT], 0); + p = gins(AAND, &r, &r); + p->scond = C_SCOND_EQ; + } p = gins(ABL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index a47de23bdb..36d9dce466 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -82,6 +82,19 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == 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 x86 NOP that we will have the right line number. + // x86 NOP 0x90 is really XCHG AX, AX; use that description + // because the NOP pseudo-instruction would be removed by + // the linker. + nodreg(®, types[TINT], D_AX); + gins(AXCHGL, ®, ®); + } p = gins(ACALL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 60b22bbea2..4dec3c8082 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -126,6 +126,19 @@ ginscall(Node *f, int proc) case 0: // normal call case -1: // normal call but no return if(f->op == ONAME && f->class == PFUNC) { + if(f == 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 x86 NOP that we will have the right line number. + // x86 NOP 0x90 is really XCHG AX, AX; use that description + // because the NOP pseudo-instruction will be removed by + // the linker. + nodreg(®, types[TINT], D_AX); + gins(AXCHGL, ®, ®); + } p = gins(ACALL, N, f); afunclit(&p->to, f); if(proc == -1 || noreturn(p)) diff --git a/test/fixedbugs/issue5856.go b/test/fixedbugs/issue5856.go new file mode 100644 index 0000000000..35cadf8c9e --- /dev/null +++ b/test/fixedbugs/issue5856.go @@ -0,0 +1,38 @@ +// run + +// Copyright 2013 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. + +package main + +import ( + "fmt" + "os" + "runtime" + "strings" +) + +func main() { + f() + panic("deferred function not run") +} + +var x = 1 + +func f() { + if x == 0 { + return + } + defer g() + panic("panic") +} + +func g() { + _, file, line, _ := runtime.Caller(2) + if !strings.HasSuffix(file, "issue5856.go") || line != 28 { + fmt.Printf("BUG: defer called from %s:%d, want issue5856.go:28\n", file, line) + os.Exit(1) + } + os.Exit(0) +}