Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/compile: DWARF IsStmt flag not set for line although it is executed #45680

Open
mahdi-hm opened this issue Apr 21, 2021 · 3 comments
Open

cmd/compile: DWARF IsStmt flag not set for line although it is executed #45680

mahdi-hm opened this issue Apr 21, 2021 · 3 comments

Comments

@mahdi-hm
Copy link

@mahdi-hm mahdi-hm commented Apr 21, 2021

What version of Go are you using (go version)?

$ go version
go version go1.16.3 linux/amd64

Does this issue reproduce with the latest release?

It was working on 1.14.9 but not after that

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/mahdi/.cache/go-build"
GOENV="/home/mahdi/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/mahdi/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/mahdi/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16.3"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1744891559=/tmp/go-build -gno-record-gcc-switches"

What did you do?

for some sample code I tried to read the dwarf information however in below specific code the line number information is wrong

package main

import "fmt"

type t2 struct {
	i int
	j [2]int
}

func printType(tt t2) {
	fmt.Println(tt.i)
	fmt.Println(tt.i)
}

func main() {
	tt := t2{
		i: 2,
	}
	printType(tt) // <-- Line 19 with the problem
}

I get dwarf line info using objdump --dwarf=decodedline hello2

What did you expect to see?

Line 19 is desirable

File name                            Line number    Starting address    View    Stmt
hello2.go                                     15            0x497660               x
hello2.go                                     15            0x497673               x
hello2.go                                     19            0x497681               x
hello2.go                                     19            0x49768a
hello2.go                                     11            0x49769b               x
hello2.go                                     11            0x4976a8

What did you see instead?

Line 19 is desirable

File name                            Line number    Starting address    View    Stmt
hello2.go                                     15            0x497760               x
hello2.go                                     15            0x49776f               x
hello2.go                                     19            0x49777d
hello2.go                                     20            0x49779a               x
hello2.go                                     15            0x4977a4               x
hello2.go                                     15            0x4977ab               x

So it is not possible to put a breakpoint on line 19 for instance on debuggers

Speculation

The strange thing about this bug is that if you change the source code to be like this:

package main

import "fmt"

type t2 struct {
	i int
	j [2]int
}

func printType(tt t2) {
	fmt.Println(tt.i)
	println(tt.i)
}

func main() {
	tt := t2{
		i: 2,
	}
	printType(tt)
}

or this

package main

import "fmt"

type t2 struct {
	i int
	j []int
}

func printType(tt t2) {
	fmt.Println(tt.i)
	fmt.Println(tt.i)
}

func main() {
	tt := t2{
		i: 2,
	}
	printType(tt)
}

the line number information is right.
So passing struct which has array elements or complex64 to a function which does two fmt.Println call make the line number information for the calling line, wrong

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Apr 21, 2021

CC @thanm

Loading

@ianlancetaylor ianlancetaylor changed the title ld: wrong dwarf information generation - Line number information issue cmd/compile: DWARF IsStmt flag not set for line although it is executed Apr 21, 2021
@ianlancetaylor ianlancetaylor added this to the Backlog milestone Apr 21, 2021
@thanm
Copy link
Contributor

@thanm thanm commented Apr 22, 2021

I can reproduce this problem in 1.16 and on tip.

The problem seems to be happening in the expand_calls phase. Looking at the SSA for main.main, prior to expand_calls we have:

  v12 (19) = LocalAddr <*t2> {tt} v2 v11
  v13 (+19) = Dereference <t2> v12 v11
  v14 (19) = StaticLECall <mem> {AuxCall{"".printType}} [24] v13 v11

with IsStmt flag set on the deref prior to the call (call itself does not have flag set). In expand_calls, the Deref is rewritten to a Move, and in the process the IsStmt flag is explicitly turned off (https://go.googlesource.com/go/+/f12cf7694f491a13bb39741d183034a917a994b3/src/cmd/compile/internal/ssa/expand_calls.go#634). SSA after expand calls looks like

v12 (19) = Move <mem> {t2} [24] v16 v11 v10
v13 (19) = StaticCall <mem> {AuxCall{"".printType([t2,0])}} [24] v12
v14 (19) = Copy <mem> v13

Note v14 above -- before it had "IstStmt" and now it does not (which makes sense given line 634).

Resulting final assembly for Go 1.16 is:

00000 (15) TEXT "".main(SB), ABIInternal
00001 (15) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
00002 (15) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
00003 (19) MOVQ $0, (SP)
00004 (19) XORPS X0, X0
00005 (19) MOVUPS X0, 8(SP)
00006 (19) MOVQ $2, (SP)
00007 (19) PCDATA $1, $0
00008 (19) CALL "".printType(SB)
00009 (+20) RET
00010 (?) END

which explains why we can't set a breakpoint on line 19.

On tip things are also unhappy, however in a slightly different and interesting way (worse in fact):

At the trim phase we have:

  v19 (19) = LEAQ <*t2> [8] v2 : AX
  v20 (19) = MOVOstorezero <mem> v19 v10
  v13 (19) = MOVQstoreconst <mem> [val=2,off=0] v2 v20
  v14 (19) = CALLstatic <mem> {AuxCall{"".printType}} [24] v13
  v15 (19) = SelectN <mem> [0] v14
  v16 (+20) = MakeResult <mem> v15
  Ret v16 (20)

which is more or less what was happening in 1.16 modulo reg abi (e.g. the IsStmt marker on the Deref has been lost), but then the final assembly looks like:

  00000 (15) TEXT "".main(SB), ABIInternal
  00001 (15) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
  00002 (15) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
  00003 (+20) MOVQ $0, (SP)
  00004 (19) LEAQ 8(SP), AX
  00005 (19) MOVUPS X15, (AX)
  00006 (19) MOVQ $2, (SP)
  00007 (19) PCDATA $1, $0
  00008 (19) CALL "".printType(SB)
  00009 (20) RET
  00010 (?) END

Note the IsStmt + line 20 on the MOVQ at 00003. This is a good deal more unhappy than what we had in 1.16. I think the weird position in the MOVQ is being introduced by this code, first

https://go.googlesource.com/go/+/5f1df260a91183c605c08af7b00741d2761b84e4/src/cmd/compile/internal/ssagen/ssa.go#6602

and then

https://go.googlesource.com/go/+/5f1df260a91183c605c08af7b00741d2761b84e4/src/cmd/compile/internal/ssagen/ssa.go#6679

Requesting help from @dr2chase , author of expand_calls and also the firstPos code in ssagen/ssa.go lines 6602/6679.

Loading

@thanm
Copy link
Contributor

@thanm thanm commented Apr 22, 2021

I wonder if it might make sense to have expand_calls make use of f.cachedLineStarts (similar to what's done in rewrite and deadcode).

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants