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: optimize dead store #50325

Open
katsusan opened this issue Dec 23, 2021 · 3 comments
Open

cmd/compile: optimize dead store #50325

katsusan opened this issue Dec 23, 2021 · 3 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Milestone

Comments

@katsusan
Copy link

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

$ go version
go version go1.17.5 linux/amd64

Does this issue reproduce with the latest release?

Yes

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

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/root/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/root/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17.5"
GCCGO="/usr/local/bin/gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/root/trygo/go.mod"
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-build2126714397=/tmp/go-build -gno-record-gcc-switches"

What did you do?

  1. try following code:
➜  trygo more x.go
package main

func main() {
        x()
}

func x() (i int) {
        i = 5
        i = 67
        p := &i
        *p = 12
        return
}
  1. use go tool compile -S x.go to see generated assembly code of function x.

What did you expect to see?

The multiple times of assignment can be optimized to MOVQ $12, AX in regABI,
with three reasons:

  • variable i is located in local stack frame.
  • the assignations of i=0(zeroing)/i=5/i=67 are not read by any subsequent instruction.
  • no explicit memory barriers(I'm not sure if atomic.Store operations can be optimzed here).

What did you see instead?

"".x STEXT nosplit size=61 args=0x0 locals=0x10 funcid=0x0
        0x0000 00000 (x.go:7)   TEXT    "".x(SB), NOSPLIT|ABIInternal, $16-0
        0x0000 00000 (x.go:7)   SUBQ    $16, SP
        0x0004 00004 (x.go:7)   MOVQ    BP, 8(SP)
        0x0009 00009 (x.go:7)   LEAQ    8(SP), BP
        0x000e 00014 (x.go:7)   FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x000e 00014 (x.go:7)   FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x000e 00014 (x.go:7)   MOVQ    $0, "".i(SP)
        0x0016 00022 (x.go:8)   MOVQ    $5, "".i(SP)
        0x001e 00030 (x.go:9)   MOVQ    $67, "".i(SP)
        0x0026 00038 (x.go:11)  MOVQ    $12, "".i(SP)
        0x002e 00046 (x.go:12)  MOVL    $12, AX
        0x0033 00051 (x.go:12)  MOVQ    8(SP), BP
        0x0038 00056 (x.go:12)  ADDQ    $16, SP
        0x003c 00060 (x.go:12)  RET
@randall77
Copy link
Contributor

Taking the address of a variable turns off a bunch of optimizations like dead store elimination. We'd have to figure out how to get around that limitation somehow. it isn't terribly easy, as it depends on when the address is taken.

The OP's example is kind of a contrived case, but I'm sure this case happens all the time:

func f() {
	var i int
	i = 5
	g(&i)
}

//go:noescape
func g(p *int)

generates

	0x0014 00020 (tmp1.go:4)	MOVQ	$0, "".i+8(SP)
	0x001d 00029 (tmp1.go:5)	MOVQ	$5, "".i+8(SP)
	0x0026 00038 (tmp1.go:6)	LEAQ	"".i+8(SP), AX
	0x002b 00043 (tmp1.go:6)	CALL	"".g(SB)

The zeroing is not needed.

@randall77 randall77 added this to the Unplanned milestone Dec 23, 2021
@dmitshur dmitshur added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 23, 2021
@katsusan
Copy link
Author

Yeah, the example originates from a case in which the address is captured by closure,

func total() (sum int) {   → MOVQ $0, "".sum+8(SP)
    sum = 100MOVQ $100, "".sum+8(SP)
    defer func() {
        sum *= 2
    }()
    ...
    return

or conversion from concrete type to interface.

type dummy struct{int8}

func x(i int) {
    var r interface{} = dummy{int8(i)}   → MOVB $0, ""..autotmp_2+31(SP)
                                         → MOVB AL, ""..autotmp_2+31(SP)
    fmt.Println(r)
}

@mariecurried
Copy link

On tip it compiles to:

PUSHQ   BP
MOVQ    SP, BP
SUBQ    $8, SP
MOVQ    $12, command-line-arguments.i(SP)
MOVL    $12, AX
ADDQ    $8, SP
POPQ    BP
RET

Now the only redundant instructions seem to be MOVQ $12, command-line-arguments.i(SP) and the stack code related to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Performance
Projects
None yet
Development

No branches or pull requests

5 participants