mirror of
https://github.com/golang/go
synced 2024-11-18 05:54:49 -07:00
cmd/compile: add test case for issue 38068
New test case for issue 38068, which deals with build reproducibility: do a pair of compilations, the first with the concurrent back end turned on, and the second with -c=1, then check to make sure we get the same output (using a test case that triggers late inlining into wrapper methods). Updates #38068. Change-Id: I4afaf78898706a66985f09d18f6f6f29876c9017 Reviewed-on: https://go-review.googlesource.com/c/go/+/234417 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
e8f5a33191
commit
6851a55f28
@ -60,3 +60,53 @@ func TestReproducibleBuilds(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue38068(t *testing.T) {
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Compile a small package with and without the concurrent
|
||||||
|
// backend, then check to make sure that the resulting archives
|
||||||
|
// are identical. Note: this uses "go tool compile" instead of
|
||||||
|
// "go build" since the latter will generate differnent build IDs
|
||||||
|
// if it sees different command line flags.
|
||||||
|
scenarios := []struct {
|
||||||
|
tag string
|
||||||
|
args string
|
||||||
|
libpath string
|
||||||
|
}{
|
||||||
|
{tag: "serial", args: "-c=1"},
|
||||||
|
{tag: "concurrent", args: "-c=2"}}
|
||||||
|
|
||||||
|
tmpdir, err := ioutil.TempDir("", "TestIssue38068")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
src := filepath.Join("testdata", "reproducible", "issue38068.go")
|
||||||
|
for i := range scenarios {
|
||||||
|
s := &scenarios[i]
|
||||||
|
s.libpath = filepath.Join(tmpdir, s.tag+".a")
|
||||||
|
// Note: use of "-p" required in order for DWARF to be generated.
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-trimpath", "-p=issue38068", "-buildid=", s.args, "-o", s.libpath, src)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readBytes := func(fn string) []byte {
|
||||||
|
payload, err := ioutil.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read executable '%s': %v", fn, err)
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
b1 := readBytes(scenarios[0].libpath)
|
||||||
|
b2 := readBytes(scenarios[1].libpath)
|
||||||
|
if !bytes.Equal(b1, b2) {
|
||||||
|
t.Fatalf("concurrent and serial builds produced different output")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
70
src/cmd/compile/internal/gc/testdata/reproducible/issue38068.go
vendored
Normal file
70
src/cmd/compile/internal/gc/testdata/reproducible/issue38068.go
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2020 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 issue38068
|
||||||
|
|
||||||
|
// A type with a couple of inlinable, non-pointer-receiver methods
|
||||||
|
// that have params and local variables.
|
||||||
|
type A struct {
|
||||||
|
s string
|
||||||
|
next *A
|
||||||
|
prev *A
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlinable, value-received method with locals and parms.
|
||||||
|
func (a A) double(x string, y int) string {
|
||||||
|
if y == 191 {
|
||||||
|
a.s = ""
|
||||||
|
}
|
||||||
|
q := a.s + "a"
|
||||||
|
r := a.s + "b"
|
||||||
|
return q + r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inlinable, value-received method with locals and parms.
|
||||||
|
func (a A) triple(x string, y int) string {
|
||||||
|
q := a.s
|
||||||
|
if y == 998877 {
|
||||||
|
a.s = x
|
||||||
|
}
|
||||||
|
r := a.s + a.s
|
||||||
|
return q + r
|
||||||
|
}
|
||||||
|
|
||||||
|
type methods struct {
|
||||||
|
m1 func(a *A, x string, y int) string
|
||||||
|
m2 func(a *A, x string, y int) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now a function that makes references to the methods via pointers,
|
||||||
|
// which should trigger the wrapper generation.
|
||||||
|
func P(a *A, ms *methods) {
|
||||||
|
if a != nil {
|
||||||
|
defer func() { println("done") }()
|
||||||
|
}
|
||||||
|
println(ms.m1(a, "a", 2))
|
||||||
|
println(ms.m2(a, "b", 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func G(x *A, n int) {
|
||||||
|
if n <= 0 {
|
||||||
|
println(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Address-taken local of type A, which will insure that the
|
||||||
|
// compiler's dtypesym() routine will create a method wrapper.
|
||||||
|
var a, b A
|
||||||
|
a.next = x
|
||||||
|
a.prev = &b
|
||||||
|
x = &a
|
||||||
|
G(x, n-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var M methods
|
||||||
|
|
||||||
|
func F() {
|
||||||
|
M.m1 = (*A).double
|
||||||
|
M.m2 = (*A).triple
|
||||||
|
G(nil, 100)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user