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: unusual line numbers associated with generated code #15453

Closed
griesemer opened this issue Apr 26, 2016 · 9 comments
Closed

cmd/compile: unusual line numbers associated with generated code #15453

griesemer opened this issue Apr 26, 2016 · 9 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@griesemer
Copy link
Contributor

griesemer commented Apr 26, 2016

The assembly listing of go/internal/gcimporter shows some unusual (wrong?) line numbers associated with the generated code. In the code for the method importer.obj we see references to gcimporter.go, even though there is no code here that should be coming from that file (also no inlining).
Here a snippet compiled with -ssa=0:

    0x01d1 00465 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  CMPB    runtime.writeBarrier(SB), $0
    0x01d8 00472 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  JNE $0, 551
    0x01da 00474 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  MOVQ    BP, 48(AX)
    0x01de 00478 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:935)   NOP
    0x01de 00478 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:935)   MOVL    $0, 56(AX)
    0x01e5 00485 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:935)   NOP
    0x01e5 00485 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:935)   MOVQ    $0, 64(AX)
    0x01ed 00493 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  MOVQ    "".p+488(FP), BX
    0x01f5 00501 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  MOVQ    BX, (SP)
    0x01f9 00505 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:193)  LEAQ    go.itab.*go/types.Func,go/types.

Here a snippet compiled with ssa enabled:

    0x0154 00340 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    "".typ·5.data+176(SP), CX
    0x015c 00348 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    CX, 48(AX)
    0x0160 00352 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVL    $0, 56(AX)
    0x0167 00359 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    $0, 64(AX)
    0x016f 00367 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    "".p+280(FP), CX
    0x0177 00375 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    CX, (SP)

And here a snippet compiled using the binary export format:

    0x00e2 00226 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  XORPS   X0, X0
    0x00e5 00229 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  DUFFZERO    $277
    0x00ea 00234 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    $0, (AX)
    0x00f1 00241 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    $0, 8(AX)
    0x00f9 00249 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVL    runtime.writeBarrier(SB), CX
    0x00ff 00255 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   LEAQ    16(AX), DX
    0x0103 00259 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   TESTB   CL, CL
    0x0105 00261 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   JNE $0, 474
    0x010b 00267 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    go/types.pkg·3+200(SP), CX
    0x0113 00275 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    CX, 16(AX)
    0x0117 00279 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    "".name·4.len+80(SP), CX
    0x011c 00284 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    CX, 32(AX)
    0x0120 00288 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVL    runtime.writeBarrier(SB), CX
    0x0126 00294 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   LEAQ    24(AX), DX
    0x012a 00298 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   TESTB   CL, CL
    0x012c 00300 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   JNE $0, 442
    0x0132 00306 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    "".name·4.ptr+232(SP), CX
    0x013a 00314 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    CX, 24(AX)
    0x013e 00318 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    "".typ·5.itab+152(SP), CX
    0x0146 00326 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    CX, 40(AX)
    0x014a 00330 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVL    runtime.writeBarrier(SB), CX
    0x0150 00336 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   TESTB   CL, CL
    0x0152 00338 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   JNE $0, 409
    0x0154 00340 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    "".typ·5.data+176(SP), CX
    0x015c 00348 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    CX, 48(AX)
    0x0160 00352 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVL    $0, 56(AX)
    0x0167 00359 (/Users/gri/go/src/go/internal/gcimporter/gcimporter.go:882)   MOVQ    $0, 64(AX)
    0x016f 00367 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    "".p+280(FP), CX
    0x0177 00375 (/Users/gri/go/src/go/internal/gcimporter/bimport.go:185)  MOVQ    CX, (SP)

The issue appears independent of ssa and independent of the export format chosen, but it somewhat affected by both. This may be all correct but needs an explanation, or there may be an issue and we should fix it.

@griesemer griesemer added this to the Go1.7Maybe milestone Apr 26, 2016
@griesemer griesemer changed the title cmd/compile: wrong (?) line numbers associated with generated code cmd/compile: unusual line numbers associated with generated code Apr 26, 2016
@mdempsky
Copy link
Member

Happens with Go 1.6 too.

@mdempsky
Copy link
Member

Here's at least my current understanding of the issue. Still investigating a fix.

When we inline a function call, we clone the function body and reassign all of the line numbers for the cloned Nodes to the function call site. This is implemented by (*inlsubst).node and setlno in inl.go, respectively.

The problem is setlno renumbers some nodes that weren't cloned by (*inlsubst).node.

For example, in the go/internal/gcimporter case @griesemer pointed out above, there are a few call sites for this inlineable go/types function:

func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
    return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}}
}

(*inlsubst).node decides to reuse the OLITERAL nodes that represent 0 and token.NoPos, but then setlno still renumbers them. This causes all of the inlined calls then to end up using the last call site's line number for these nodes.

@gopherbot
Copy link

CL https://golang.org/cl/22123 mentions this issue.

@mdempsky
Copy link
Member

mdempsky commented Apr 27, 2016

Minimized repro case:

package p

var A, B interface{}

func F() {
    A = New()
    B = New()
}

type S struct {
    x uint32
}

func New() *S {
    return &S{0}
}

Compile with go tool compile -S -ssa=0 repro.go and see:

    0x0017 00023 (repro.go:6)       LEAQ    type."".S(SB), BX
    0x001e 00030 (repro.go:6)       MOVQ    BX, (SP)
    0x0022 00034 (repro.go:6)       PCDATA  $0, $0
    0x0022 00034 (repro.go:6)       CALL    runtime.newobject(SB)
    0x0027 00039 (repro.go:6)       MOVQ    8(SP), CX
    0x002c 00044 (repro.go:7)       NOP
    0x002c 00044 (repro.go:7)       MOVL    $0, (CX)
    0x0032 00050 (repro.go:6)       LEAQ    type.*"".S(SB), BX
    0x0039 00057 (repro.go:6)       MOVQ    BX, "".autotmp_6+16(SP)
    0x003e 00062 (repro.go:6)       MOVQ    BX, "".A(SB)
    0x0045 00069 (repro.go:6)       MOVQ    CX, "".autotmp_6+24(SP)
    0x004a 00074 (repro.go:6)       CMPB    runtime.writeBarrier(SB), $0
    0x0051 00081 (repro.go:6)       JNE     $0, 185
    0x0053 00083 (repro.go:6)       MOVQ    CX, "".A+8(SB)
    0x005a 00090 (repro.go:6)       NOP

The lines with repro.go:7 should say repro.go:6.

mk0x9 pushed a commit to mk0x9/go that referenced this issue Apr 27, 2016
builtin.go was auto-generated via go generate; all other
changes were manual.

The new format reduces the export data size by ~65% on average
for the std library packages (and there is still quite a bit of
room for improvement).

The average time to write export data is reduced by (at least)
62% as measured in one run over the std lib, it is likely more.

The average time to read import data is reduced by (at least)
37% as measured in one run over the std lib, it is likely more.
There is also room to improve this time.

The compiler transparently handles both packages using the old
and the new format.

Comparing the -S output of the go build for each package via
the cmp.bash script (added) shows identical assembly code for
all packages, but 6 files show file:line differences:

The following files have differences because they use cgo
and cgo uses different temp. directories for different builds.
Harmless.

	src/crypto/x509
	src/net
	src/os/user
	src/runtime/cgo

The following files have file:line differences that are not yet
fully explained; however the differences exist w/ and w/o new export
format (pre-existing condition). See issue golang#15453.

	src/go/internal/gccgoimporter
	src/go/internal/gcimporter

In summary, switching to the new export format produces the same
package files as before for all practical purposes.

How can you tell which one you have (if you care): Open a package
(.a) file in an editor. Textual export data starts with a $$ after
the header and is more or less legible; binary export data starts
with a $$B after the header and is mostly unreadable. A stand-alone
decoder (for debugging) is in the works.

In case of a problem, please first try reverting back to the old
textual format to determine if the cause is the new export format:

For a stand-alone compiler invocation:
- go tool compile -newexport=0 <files>

For a single package:
- go build -gcflags="-newexport=0" <pkg>

For make/all.bash:
- (export GO_GCFLAGS="-newexport=0"; sh make.bash)

Fixes golang#13241.

Change-Id: I2588cb463be80af22446bf80c225e92ab79878b8
Reviewed-on: https://go-review.googlesource.com/22123
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
@mdempsky
Copy link
Member

Elaborating a bit more, the problem is that sinit.go uses the OLITERAL Node's line number when desugaring OSTRUCTLIT Nodes into sequences of OAS Nodes. Two possible fixes I have in mind:

  1. Change inl.go to clone OLITERAL Nodes. This is simple and I've already experimentally confirmed it works, but I'm worried it will increases memory usage due to having to clone every constant value within inlined function bodies.

  2. Change sinit.go to use a different source for the OAS Nodes' line numbers. Maybe the enclosing OKEY Nodes? Skimming go/printer's source, I think canonical gofmt'ing should always place OKEYs on the same line as the values anyway, though someone might manually write something like:

    p := &s{
        x:
            1,
    }
    

    and the generated p.x = 1 assignment statement's line association would move from 1, to x:.

Option 2 seems preferable since it shouldn't negatively impact gofmt'd code, and it still does something reasonable for non-gofmt'd code.

@griesemer
Copy link
Contributor Author

griesemer commented Apr 28, 2016

Fix 1) seems the more correct fix, isn't it? We could see how many more nodes are created over the course of make.bash and take it from there.

Alternatively, could we do something like wrapname (parser.go:1496)? Or is that the same cost?

@mdempsky
Copy link
Member

mdempsky commented May 2, 2016

Fix 1 seems most correct to me, I'm just concerned about the compilation overhead.

Instrumenting the compiler to count the number of OLITERAL Nodes that may need copying due to inlining, it looks like maybe it would be okay. Compiling net/http would require duplicating an extra 1400 Nodes. Most other packages need closer to the order of 100 to 200. gc requires 7000.

Another option (3) is to revert golang.org/cl/11672 (55203c7), which seems to be the CL that introduced the unusual line numbers.

@rsc
Copy link
Contributor

rsc commented May 17, 2016

Let's leave for Go 1.8.

@rsc rsc modified the milestones: Go1.8, Go1.7Maybe May 17, 2016
@quentinmit quentinmit added the NeedsFix The path to resolution is known, but the work has not been done. label Oct 11, 2016
@rsc rsc modified the milestones: Go1.9, Go1.8 Oct 21, 2016
@gopherbot
Copy link

CL https://golang.org/cl/37232 mentions this issue.

@golang golang locked and limited conversation to collaborators Mar 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

5 participants