go/src/cmd/compile/internal/gc/global_test.go

117 lines
2.6 KiB
Go
Raw Normal View History

cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
// Copyright 2015 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 gc
import (
"bytes"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
"testing"
)
// Make sure "hello world" does not link in all the
// fmt.scanf routines. See issue 6853.
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
func TestScanfRemoval(t *testing.T) {
testenv.MustHaveGoBuild(t)
t.Parallel()
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
// Make a directory to work in.
dir, err := ioutil.TempDir("", "issue6853a-")
if err != nil {
t.Fatalf("could not create directory: %v", err)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
}
defer os.RemoveAll(dir)
// Create source.
src := filepath.Join(dir, "test.go")
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
f, err := os.Create(src)
if err != nil {
t.Fatalf("could not create source file: %v", err)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
}
f.Write([]byte(`
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
`))
f.Close()
// Name of destination.
dst := filepath.Join(dir, "test")
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
// Compile source.
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dst, src)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("could not build target: %v", err)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
}
// Check destination to see if scanf code was included.
cmd = exec.Command(testenv.GoToolPath(t), "tool", "nm", dst)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("could not read target: %v", err)
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
}
if bytes.Contains(out, []byte("scanInt")) {
t.Fatalf("scanf code not removed from helloworld")
cmd/compile: captureless closures are constants In particular, we can initialize globals with them at link time instead of generating code for them in an init() function. Less code, less startup cost. But the real reason for this change is binary size. This change reduces the binary size of hello world by ~4%. The culprit is fmt.ssFree, a global variable which is a sync.Pool of scratch scan states. It is initalized with a captureless closure as the pool's New action. That action in turn references all the scanf code. If you never call any of the fmt.Scanf* routines, ssFree is never used. But before this change, ssFree is still referenced by fmt's init function. That keeps ssFree and all the code it references in the binary. With this change, ssFree is initialized at link time. As a result, fmt.init never mentions ssFree. If you don't call fmt.Scanf*, ssFree is unreferenced and it and the scanf code are not included. This change is an easy fix for what is generally a much harder problem, the unnecessary initializing of unused globals (and retention of code that they reference). Ideally we should have separate init code for each global and only include that code if the corresponding global is live. (We'd need to make sure that the initializing code has no side effects, except on the global being initialized.) That is a much harder change. Update #6853 Change-Id: I19d1e33992287882c83efea6ce113b7cfc504b67 Reviewed-on: https://go-review.googlesource.com/17398 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-12-03 13:20:58 -08:00
}
}
// Make sure -S prints assembly code. See issue 14515.
func TestDashS(t *testing.T) {
testenv.MustHaveGoBuild(t)
t.Parallel()
// Make a directory to work in.
dir, err := ioutil.TempDir("", "issue14515-")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(dir)
// Create source.
src := filepath.Join(dir, "test.go")
f, err := os.Create(src)
if err != nil {
t.Fatalf("could not create source file: %v", err)
}
f.Write([]byte(`
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
`))
f.Close()
// Compile source.
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags", "-S", "-o", filepath.Join(dir, "test"), src)
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("could not build target: %v", err)
}
patterns := []string{
// It is hard to look for actual instructions in an
// arch-independent way. So we'll just look for
// pseudo-ops that are arch-independent.
"\tTEXT\t",
"\tFUNCDATA\t",
"\tPCDATA\t",
}
outstr := string(out)
for _, p := range patterns {
if !strings.Contains(outstr, p) {
println(outstr)
panic("can't find pattern " + p)
}
}
}