cmd/compile: try to preserve IsStmt marks from OpConvert

Note when a statement mark was not consumed during Prog
generation, and try to use it on a subsequent opcode so
that the statement marker will not be lost.

And a test.

Fixes #49628.

Change-Id: I03f7782a9809cc4a0a5870df92b3e182cf124554
Reviewed-on: https://go-review.googlesource.com/c/go/+/366694
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Dan Scales <danscales@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
David Chase 2021-11-23 13:29:13 -05:00
parent 7b7efd7a7c
commit 61011de1af
3 changed files with 52 additions and 0 deletions

View File

@ -82,6 +82,25 @@ func TestDebugLinesPushback(t *testing.T) {
} }
} }
func TestDebugLinesConvert(t *testing.T) {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows.
t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin")
}
switch testGoArch() {
default:
t.Skip("skipped for many architectures")
case "arm64", "amd64": // register ABI
fn := "G[go.shape.int_0]"
if buildcfg.Experiment.Unified {
// Unified mangles differently
fn = "G[int]"
}
testDebugLines(t, "-N -l -G=3", "convertline.go", fn, []int{9, 10, 11}, true)
}
}
func TestInlineLines(t *testing.T) { func TestInlineLines(t *testing.T) {
if runtime.GOARCH != "amd64" && *testGoArchFlag == "" { if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
// As of september 2021, works for everything except mips64, but still potentially fragile // As of september 2021, works for everything except mips64, but still potentially fragile

View File

@ -0,0 +1,16 @@
package main
import "fmt"
func F[T any](n T) {
fmt.Printf("called\n")
}
func G[T any](n T) {
F(n)
fmt.Printf("after\n")
}
func main() {
G(3)
}

View File

@ -6577,6 +6577,22 @@ func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) {
// explicit statement boundaries should appear // explicit statement boundaries should appear
// in the generated code. // in the generated code.
if p.IsStmt() != src.PosIsStmt { if p.IsStmt() != src.PosIsStmt {
if s.pp.Pos.IsStmt() == src.PosIsStmt && s.pp.Pos.SameFileAndLine(p) {
// If s.pp.Pos already has a statement mark, then it was set here (below) for
// the previous value. If an actual instruction had been emitted for that
// value, then the statement mark would have been reset. Since the statement
// mark of s.pp.Pos was not reset, this position (file/line) still needs a
// statement mark on an instruction. If file and line for this value are
// the same as the previous value, then the first instruction for this
// value will work to take the statement mark. Return early to avoid
// resetting the statement mark.
//
// The reset of s.pp.Pos occurs in (*Progs).Prog() -- if it emits
// an instruction, and the instruction's statement mark was set,
// and it is not one of the LosesStmtMark instructions,
// then Prog() resets the statement mark on the (*Progs).Pos.
return
}
p = p.WithNotStmt() p = p.WithNotStmt()
// Calls use the pos attached to v, but copy the statement mark from State // Calls use the pos attached to v, but copy the statement mark from State
} }
@ -6818,6 +6834,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
for i, b := range f.Blocks { for i, b := range f.Blocks {
s.bstart[b.ID] = s.pp.Next s.bstart[b.ID] = s.pp.Next
s.lineRunStart = nil s.lineRunStart = nil
s.SetPos(s.pp.Pos.WithNotStmt()) // It needs a non-empty Pos, but cannot be a statement boundary (yet).
// Attach a "default" liveness info. Normally this will be // Attach a "default" liveness info. Normally this will be
// overwritten in the Values loop below for each Value. But // overwritten in the Values loop below for each Value. But