From c1c5d479bd9ead47f718156866c8bd188a8e19b8 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Sep 2014 13:16:43 -0400 Subject: cmd/5g, cmd/8g: make 'out of registers' a fatal error There's no point in continuing. We will only get confused. 6g already makes this fatal. LGTM=dave, minux, iant R=iant, dave, minux CC=golang-codereviews https://golang.org/cl/140660043 --- src/cmd/5g/gsubr.c | 4 ++-- src/cmd/8g/gsubr.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 93bfafef66..06e274e14d 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -361,7 +361,7 @@ regalloc(Node *n, Type *t, Node *o) print("registers allocated at\n"); for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) print("%d %p\n", i, regpc[i]); - yyerror("out of fixed registers"); + fatal("out of fixed registers"); goto err; case TFLOAT32: @@ -374,7 +374,7 @@ regalloc(Node *n, Type *t, Node *o) for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++) if(reg[i] == 0) goto out; - yyerror("out of floating point registers"); + fatal("out of floating point registers"); goto err; case TCOMPLEX64: diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index a83d048f40..3077e0ad9c 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -938,7 +938,7 @@ regalloc(Node *n, Type *t, Node *o) fprint(2, "registers allocated at\n"); for(i=D_AX; i<=D_DI; i++) fprint(2, "\t%R\t%#lux\n", i, regpc[i]); - yyerror("out of fixed registers"); + fatal("out of fixed registers"); goto err; case TFLOAT32: -- cgit v1.3 From 4caf377e6ef53356414b185930e898acb896c562 Mon Sep 17 00:00:00 2001 From: Ahmed Waheed Moanes Date: Tue, 16 Sep 2014 15:06:52 -0700 Subject: cmd/go: use pkg-config include pathes in swig and don't double compile c++ files. Fixes #8566. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/126210045 --- src/cmd/go/build.go | 93 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 32 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 45b5bc3233..2e52731529 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -824,12 +824,17 @@ func (b *builder) build(a *action) (err error) { } } - var gofiles, cfiles, sfiles, objects, cgoObjects []string + var gofiles, cfiles, sfiles, objects, cgoObjects, pcCFLAGS, pcLDFLAGS []string gofiles = append(gofiles, a.p.GoFiles...) cfiles = append(cfiles, a.p.CFiles...) sfiles = append(sfiles, a.p.SFiles...) + if a.p.usesCgo() || a.p.usesSwig() { + if pcCFLAGS, pcLDFLAGS, err = b.getPkgConfigFlags(a.p); err != nil { + return + } + } // Run cgo. if a.p.usesCgo() { // In a package using cgo, cgo compiles the C, C++ and assembly files with gcc. @@ -860,7 +865,7 @@ func (b *builder) build(a *action) (err error) { if a.cgo != nil && a.cgo.target != "" { cgoExe = a.cgo.target } - outGo, outObj, err := b.cgo(a.p, cgoExe, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) + outGo, outObj, err := b.cgo(a.p, cgoExe, obj, pcCFLAGS, pcLDFLAGS, gccfiles, a.p.CXXFiles, a.p.MFiles) if err != nil { return err } @@ -873,9 +878,18 @@ func (b *builder) build(a *action) (err error) { // In a package using SWIG, any .c or .s files are // compiled with gcc. gccfiles := append(cfiles, sfiles...) + cxxfiles, mfiles := a.p.CXXFiles, a.p.MFiles cfiles = nil sfiles = nil - outGo, outObj, err := b.swig(a.p, obj, gccfiles, a.p.CXXFiles, a.p.MFiles) + + // Don't build c/c++ files twice if cgo is enabled (mainly for pkg-config). + if a.p.usesCgo() { + cxxfiles = nil + gccfiles = nil + mfiles = nil + } + + outGo, outObj, err := b.swig(a.p, obj, pcCFLAGS, gccfiles, cxxfiles, mfiles) if err != nil { return err } @@ -1019,6 +1033,34 @@ func (b *builder) build(a *action) (err error) { return nil } +// Calls pkg-config if needed and returns the cflags/ldflags needed to build the package. +func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err error) { + if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { + var out []byte + out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + err = errPrintedOutput + return + } + if len(out) > 0 { + cflags = strings.Fields(string(out)) + } + out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) + if err != nil { + b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) + b.print(err.Error() + "\n") + err = errPrintedOutput + return + } + if len(out) > 0 { + ldflags = strings.Fields(string(out)) + } + } + return +} + // install is the action for installing a single package or executable. func (b *builder) install(a *action) (err error) { defer func() { @@ -2100,36 +2142,16 @@ var ( cgoLibGccFileOnce sync.Once ) -func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { +func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true) _, cgoexeCFLAGS, _, _ := b.cflags(p, false) - + cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...) + cgoLDFLAGS = append(cgoLDFLAGS, pcLDFLAGS...) // If we are compiling Objective-C code, then we need to link against libobjc if len(mfiles) > 0 { cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc") } - if pkgs := p.CgoPkgConfig; len(pkgs) > 0 { - out, err := b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--cflags", pkgs) - if err != nil { - b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)) - b.print(err.Error() + "\n") - return nil, nil, errPrintedOutput - } - if len(out) > 0 { - cgoCPPFLAGS = append(cgoCPPFLAGS, strings.Fields(string(out))...) - } - out, err = b.runOut(p.Dir, p.ImportPath, nil, "pkg-config", "--libs", pkgs) - if err != nil { - b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out)) - b.print(err.Error() + "\n") - return nil, nil, errPrintedOutput - } - if len(out) > 0 { - cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...) - } - } - // Allows including _cgo_export.h from .[ch] files in the package. cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj) @@ -2344,7 +2366,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles // Run SWIG on all SWIG input files. // TODO: Don't build a shared library, once SWIG emits the necessary // pragmas for external linking. -func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { +func (b *builder) swig(p *Package, obj string, pcCFLAGS, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) cflags := stringList(cgoCPPFLAGS, cgoCFLAGS) cxxflags := stringList(cgoCPPFLAGS, cgoCXXFLAGS) @@ -2385,7 +2407,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri } for _, f := range p.SwigFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, false, intgosize) + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, false, intgosize) if err != nil { return nil, nil, err } @@ -2400,7 +2422,7 @@ func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []stri } } for _, f := range p.SwigCXXFiles { - goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, true, intgosize) + goFile, objFile, gccObjFile, err := b.swigOne(p, f, obj, pcCFLAGS, true, intgosize) if err != nil { return nil, nil, err } @@ -2479,13 +2501,13 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) { } // Run SWIG on one SWIG input file. -func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { +func (b *builder) swigOne(p *Package, file, obj string, pcCFLAGS []string, cxx bool, intgosize string) (outGo, outObj, objGccObj string, err error) { cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, _ := b.cflags(p, true) var cflags []string if cxx { - cflags = stringList(cgoCPPFLAGS, cgoCXXFLAGS) + cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCXXFLAGS) } else { - cflags = stringList(cgoCPPFLAGS, cgoCFLAGS) + cflags = stringList(cgoCPPFLAGS, pcCFLAGS, cgoCFLAGS) } n := 5 // length of ".swig" @@ -2511,6 +2533,13 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri "-o", obj + gccBase + gccExt, "-outdir", obj, } + + for _, f := range cflags { + if len(f) > 3 && f[:2] == "-I" { + args = append(args, f) + } + } + if gccgo { args = append(args, "-gccgo") if pkgpath := gccgoPkgpath(p); pkgpath != "" { -- cgit v1.3 From 5a40b568d023922e735ddc21f3b4a30d52197b7a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 18 Sep 2014 17:27:26 -0700 Subject: lib9, cmd/ld: fixes for cross-linking on a Windows host This fixes a couple of problems that occur when the linker removes its temporary directory on Windows. The linker only creates and removes a temporary directory when doing external linking. Windows does not yet support external linking. Therefore, these problems are only seen when using a cross-compiler hosted on Windows. In lib9, FindFirstFileW returns just the file name, not the full path name. Don't assume that we will find a slash. Changed the code to work either way just in case. In ld, Windows requires that files be closed before they are removed, so close the output file before we might try to remove it. Fixes #8723. LGTM=alex.brainman R=golang-codereviews, alex.brainman CC=golang-codereviews https://golang.org/cl/141690043 --- src/cmd/ld/lib.c | 4 ++++ src/lib9/tempdir_windows.c | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 651705a2e6..36f0f99de2 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -144,6 +144,10 @@ libinit(void) void errorexit(void) { + if(cout >= 0) { + // For rmtemp run at atexit time on Windows. + close(cout); + } if(nerrors) { if(cout >= 0) mayberemoveoutfile(); diff --git a/src/lib9/tempdir_windows.c b/src/lib9/tempdir_windows.c index 1a530059ae..4c3df7cf11 100644 --- a/src/lib9/tempdir_windows.c +++ b/src/lib9/tempdir_windows.c @@ -70,7 +70,7 @@ removeall(char *p) { WinRune *r, *r1; DWORD attr; - char *q, *elem; + char *q, *qt, *elem; HANDLE h; WIN32_FIND_DATAW data; @@ -91,15 +91,18 @@ removeall(char *p) do{ q = toutf(data.cFileName); elem = strrchr(q, '\\'); - if(elem != nil) { + if(elem != nil) elem++; - if(strcmp(elem, ".") == 0 || strcmp(elem, "..") == 0) { - free(q); - continue; - } + else + elem = q; + if(strcmp(elem, ".") == 0 || strcmp(elem, "..") == 0) { + free(q); + continue; } - removeall(q); - free(q); + qt = smprint("%s\\%s", p, q); + free(q); + removeall(qt); + free(qt); }while(FindNextFileW(h, &data)); FindClose(h); -- cgit v1.3 From 182d1316dd975f426451cee34ba2e3e0953e084f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 19 Sep 2014 13:51:06 -0400 Subject: cmd/go, testing: add TestMain support Fixes #8202. LGTM=r, bradfitz R=r, josharian, bradfitz CC=golang-codereviews https://golang.org/cl/148770043 --- doc/go1.4.txt | 1 + src/cmd/go/test.go | 46 +++++++++++++++++++++++++++++++++++-- src/testing/testing.go | 56 +++++++++++++++++++++++++++++++++++++++++---- src/testing/testing_test.go | 18 +++++++++++++++ 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 src/testing/testing_test.go (limited to 'src/cmd') diff --git a/doc/go1.4.txt b/doc/go1.4.txt index 6180bc5b92..f46ef48f5d 100644 --- a/doc/go1.4.txt +++ b/doc/go1.4.txt @@ -32,6 +32,7 @@ sync/atomic: add Value (CL 136710045) syscall: Setuid, Setgid are disabled on linux platforms. On linux those syscalls operate on the calling thread, not the whole process. This does not match the semantics of other platforms, nor the expectations of the caller, so the operations have been disabled until issue 1435 is resolved (CL 106170043) syscall: now frozen (CL 129820043) testing: add Coverage (CL 98150043) +testing: add TestMain support (CL 148770043) text/scanner: add IsIdentRune field of Scanner. (CL 108030044) time: use the micro symbol (µ (U+00B5)) to print microsecond duration (CL 105030046) encoding/asn1: optional elements with a default value will now only be omitted if they have that value (CL 86960045). diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index a602469e44..e990b17bfa 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "errors" "fmt" "go/ast" "go/build" @@ -291,6 +292,7 @@ var testMainDeps = map[string]bool{ // Dependencies for testmain. "testing": true, "regexp": true, + "os": true, } func runTest(cmd *Command, args []string) { @@ -687,7 +689,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, omitDWARF: !testC && !testNeedBinary, } - // The generated main also imports testing and regexp. + // The generated main also imports testing, regexp, and os. stk.push("testmain") for dep := range testMainDeps { if dep == ptest.ImportPath { @@ -1057,6 +1059,31 @@ func (b *builder) notest(a *action) error { return nil } +// isTestMain tells whether fn is a TestMain(m *testing.Main) function. +func isTestMain(fn *ast.FuncDecl) bool { + if fn.Name.String() != "TestMain" || + fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || + fn.Type.Params == nil || + len(fn.Type.Params.List) != 1 || + len(fn.Type.Params.List[0].Names) > 1 { + return false + } + ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr) + if !ok { + return false + } + // We can't easily check that the type is *testing.M + // because we don't know how testing has been imported, + // but at least check that it's *M or *something.M. + if name, ok := ptr.X.(*ast.Ident); ok && name.Name == "M" { + return true + } + if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == "M" { + return true + } + return false +} + // isTest tells whether name looks like a test (or benchmark, according to prefix). // It is a Test (say) if there is a character after Test that is not a lower-case letter. // We don't want TesticularCancer. @@ -1113,6 +1140,7 @@ type testFuncs struct { Tests []testFunc Benchmarks []testFunc Examples []testFunc + TestMain *testFunc Package *Package ImportTest bool NeedTest bool @@ -1168,6 +1196,12 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error { } name := n.Name.String() switch { + case isTestMain(n): + if t.TestMain != nil { + return errors.New("multiple definitions of TestMain") + } + t.TestMain = &testFunc{pkg, name, ""} + *doImport, *seen = true, true case isTest(name, "Test"): t.Tests = append(t.Tests, testFunc{pkg, name, ""}) *doImport, *seen = true, true @@ -1200,6 +1234,9 @@ var testmainTmpl = template.Must(template.New("main").Parse(` package main import ( +{{if not .TestMain}} + "os" +{{end}} "regexp" "testing" @@ -1294,7 +1331,12 @@ func main() { CoveredPackages: {{printf "%q" .Covered}}, }) {{end}} - testing.Main(matchString, tests, benchmarks, examples) + m := testing.MainStart(matchString, tests, benchmarks, examples) +{{with .TestMain}} + {{.Package}}.{{.Name}}(m) +{{else}} + os.Exit(m.Run()) +{{end}} } `)) diff --git a/src/testing/testing.go b/src/testing/testing.go index 731762cb1d..21460b0ed4 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -117,6 +117,26 @@ // The entire test file is presented as the example when it contains a single // example function, at least one other function, type, variable, or constant // declaration, and no test or benchmark functions. +// +// Main +// +// It is sometimes necessary for a test program to do extra setup or teardown +// before or after testing. It is also sometimes necessary for a test to control +// which code runs on the main thread. To support these and other cases, +// if a test file contains a function: +// +// func TestMain(m *testing.M) +// +// then the generated test will call TestMain(m) instead of running the tests +// directly. TestMain runs in the main goroutine and can do whatever setup +// and teardown is necessary around a call to m.Run. It should then call +// os.Exit with the result of m.Run. +// +// The minimal implementation of TestMain is: +// +// func TestMain(m *testing.M) { os.Exit(m.Run()) } +// +// In effect, that is the implementation used when no TestMain is explicitly defined. package testing import ( @@ -431,23 +451,49 @@ func tRunner(t *T, test *InternalTest) { // An internal function but exported because it is cross-package; part of the implementation // of the "go test" command. func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { + os.Exit(MainStart(matchString, tests, benchmarks, examples).Run()) +} + +// M is a type passed to a TestMain function to run the actual tests. +type M struct { + matchString func(pat, str string) (bool, error) + tests []InternalTest + benchmarks []InternalBenchmark + examples []InternalExample +} + +// MainStart is meant for use by tests generated by 'go test'. +// It is not meant to be called directly and is not subject to the Go 1 compatibility document. +// It may change signature from release to release. +func MainStart(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M { + return &M{ + matchString: matchString, + tests: tests, + benchmarks: benchmarks, + examples: examples, + } +} + +// Run runs the tests. It returns an exit code to pass to os.Exit. +func (m *M) Run() int { flag.Parse() parseCpuList() before() startAlarm() - haveExamples = len(examples) > 0 - testOk := RunTests(matchString, tests) - exampleOk := RunExamples(matchString, examples) + haveExamples = len(m.examples) > 0 + testOk := RunTests(m.matchString, m.tests) + exampleOk := RunExamples(m.matchString, m.examples) stopAlarm() if !testOk || !exampleOk { fmt.Println("FAIL") after() - os.Exit(1) + return 1 } fmt.Println("PASS") - RunBenchmarks(matchString, benchmarks) + RunBenchmarks(m.matchString, m.benchmarks) after() + return 0 } func (t *T) report() { diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go new file mode 100644 index 0000000000..87a5c16d6e --- /dev/null +++ b/src/testing/testing_test.go @@ -0,0 +1,18 @@ +// Copyright 2014 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 testing_test + +import ( + "os" + "testing" +) + +// This is exactly what a test would do without a TestMain. +// It's here only so that there is at least one package in the +// standard library with a TestMain, so that code is executed. + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} -- cgit v1.3 From 0e1a07167b2ba0e71487feea8e2e20a2e29fdf3a Mon Sep 17 00:00:00 2001 From: Sameer Ajmani Date: Fri, 19 Sep 2014 15:59:47 -0400 Subject: cmd/go: fix typo LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/138700043 --- src/cmd/go/test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index e990b17bfa..c135b89c84 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -49,7 +49,7 @@ It prints a summary of the test results in the format: followed by detailed output for each failed package. 'Go test' recompiles each package along with any files with names matching -the file pattern "*_test.go". +the file pattern "*_test.go". Files whose names begin with "_" (including "_test.go") or "." are ignored. These additional files can contain test functions, benchmark functions, and example functions. See 'go help testfunc' for more. @@ -1059,7 +1059,7 @@ func (b *builder) notest(a *action) error { return nil } -// isTestMain tells whether fn is a TestMain(m *testing.Main) function. +// isTestMain tells whether fn is a TestMain(m *testing.M) function. func isTestMain(fn *ast.FuncDecl) bool { if fn.Name.String() != "TestMain" || fn.Type.Results != nil && len(fn.Type.Results.List) > 0 || -- cgit v1.3 From 1193993c1db83ee8c0a8e86e6d41db1dd1982002 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 23 Sep 2014 18:24:35 -0700 Subject: cmd/pack: fix c command for existing file There were at least two bugs: 1) It would overwrite a non-archive. 2) It would truncate a non-archive and then fail. In general the file handling was too clever to be correct. Make it more straightforward, doing the creation separately from archive management. Fixes #8369. LGTM=adg, iant R=golang-codereviews, adg, iant CC=golang-codereviews https://golang.org/cl/147010046 --- src/cmd/pack/doc.go | 4 ++++ src/cmd/pack/pack.go | 50 +++++++++++++++++++++++++++++++++-------------- src/cmd/pack/pack_test.go | 23 +++++++++++++++++----- 3 files changed, 57 insertions(+), 20 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go index 1529e07e90..a702594e23 100644 --- a/src/cmd/pack/doc.go +++ b/src/cmd/pack/doc.go @@ -20,6 +20,10 @@ The operation op is given by one of these letters: t list files from the archive x extract files from the archive +The archive argument to the c command must be non-existent or a +valid archive file, which will be cleared before adding new entries. It +is an error if the file exists but is not an archive. + For the p, t, and x commands, listing no names on the command line causes the operation to apply to all files in the archive. diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go index 594433712d..ffb2d617ae 100644 --- a/src/cmd/pack/pack.go +++ b/src/cmd/pack/pack.go @@ -142,16 +142,19 @@ type Archive struct { matchAll bool // match all files in archive } -// archive opens (or if necessary creates) the named archive. +// archive opens (and if necessary creates) the named archive. func archive(name string, mode int, files []string) *Archive { - fd, err := os.OpenFile(name, mode, 0) - if err != nil && mode&^os.O_TRUNC == os.O_RDWR && os.IsNotExist(err) { - fd, err = create(name) + // If the file exists, it must be an archive. If it doesn't exist, or if + // we're doing the c command, indicated by O_TRUNC, truncate the archive. + if !existingArchive(name) || mode&os.O_TRUNC != 0 { + create(name) + mode &^= os.O_TRUNC } + fd, err := os.OpenFile(name, mode, 0) if err != nil { log.Fatal(err) } - mustBeArchive(fd) + checkHeader(fd) return &Archive{ fd: fd, files: files, @@ -160,23 +163,40 @@ func archive(name string, mode int, files []string) *Archive { } // create creates and initializes an archive that does not exist. -func create(name string) (*os.File, error) { - fd, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) +func create(name string) { + fd, err := os.Create(name) if err != nil { - return nil, err + log.Fatal(err) + } + _, err = fmt.Fprint(fd, arHeader) + if err != nil { + log.Fatal(err) + } + fd.Close() +} + +// existingArchive reports whether the file exists and is a valid archive. +// If it exists but is not an archive, existingArchive will exit. +func existingArchive(name string) bool { + fd, err := os.Open(name) + if err != nil { + if os.IsNotExist(err) { + return false + } + log.Fatal("cannot open file: %s", err) } - fmt.Fprint(fd, arHeader) - fd.Seek(0, 0) - return fd, nil + checkHeader(fd) + fd.Close() + return true } -// mustBeArchive verifies the header of the file. It assumes the file offset -// is 0 coming in, and leaves it positioned immediately after the header. -func mustBeArchive(fd *os.File) { +// checkHeader verifies the header of the file. It assumes the file +// is positioned at 0 and leaves it positioned at the end of the header. +func checkHeader(fd *os.File) { buf := make([]byte, len(arHeader)) _, err := io.ReadFull(fd, buf) if err != nil || string(buf) != arHeader { - log.Fatal("file is not an archive: bad header") + log.Fatal("%s is not an archive: bad header", fd.Name()) } } diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go index e41cf3ce42..cf6121fcc1 100644 --- a/src/cmd/pack/pack_test.go +++ b/src/cmd/pack/pack_test.go @@ -56,11 +56,8 @@ func tmpDir(t *testing.T) string { return name } -// Test that we can create an archive, write to it, and get the same contents back. -// Tests the rv and then the pv command on a new archive. -func TestCreate(t *testing.T) { - dir := tmpDir(t) - defer os.RemoveAll(dir) +// testCreate creates an archive in the specified directory. +func testCreate(t *testing.T, dir string) { name := filepath.Join(dir, "pack.a") ar := archive(name, os.O_RDWR, nil) // Add an entry by hand. @@ -85,6 +82,22 @@ func TestCreate(t *testing.T) { } } +// Test that we can create an archive, write to it, and get the same contents back. +// Tests the rv and then the pv command on a new archive. +func TestCreate(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) +} + +// Test that we can create an archive twice with the same name (Issue 8369). +func TestCreateTwice(t *testing.T) { + dir := tmpDir(t) + defer os.RemoveAll(dir) + testCreate(t, dir) + testCreate(t, dir) +} + // Test that we can create an archive, put some files in it, and get back a correct listing. // Tests the tv command. func TestTableOfContents(t *testing.T) { -- cgit v1.3 From a0785a53add4253db84349d58abbe2ba8be130d9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 15:10:38 -0400 Subject: cmd/go: prohibit C sources files unless using cgo Those C files would have been compiled with 6c. It's close to impossible to use C correctly anymore, and the C compilers are going away eventually. Make them unavailable now. go1.4.txt change in CL 145890046 LGTM=iant R=iant CC=golang-codereviews, r https://golang.org/cl/149720043 --- misc/cgo/test/backdoor/backdoor.go | 3 +-- misc/cgo/test/backdoor/backdoor_gccgo.go | 11 ----------- misc/cgo/test/backdoor/runtime.c | 18 ------------------ misc/cgo/test/issue7695_test.go | 30 ------------------------------ src/cmd/go/pkg.go | 10 ++++++++++ src/cmd/go/test.bash | 14 ++++++++++++++ src/cmd/go/testdata/src/badc/x.c | 1 + src/cmd/go/testdata/src/badc/x.go | 1 + src/net/empty.c | 8 -------- src/net/empty.s | 8 ++++++++ src/runtime/debug/debug.c | 9 --------- src/runtime/debug/debug.s | 9 +++++++++ 12 files changed, 44 insertions(+), 78 deletions(-) delete mode 100644 misc/cgo/test/backdoor/backdoor_gccgo.go delete mode 100644 misc/cgo/test/backdoor/runtime.c delete mode 100644 misc/cgo/test/issue7695_test.go create mode 100644 src/cmd/go/testdata/src/badc/x.c create mode 100644 src/cmd/go/testdata/src/badc/x.go delete mode 100644 src/net/empty.c create mode 100644 src/net/empty.s delete mode 100644 src/runtime/debug/debug.c create mode 100644 src/runtime/debug/debug.s (limited to 'src/cmd') diff --git a/misc/cgo/test/backdoor/backdoor.go b/misc/cgo/test/backdoor/backdoor.go index 7398772bd2..3a973494bc 100644 --- a/misc/cgo/test/backdoor/backdoor.go +++ b/misc/cgo/test/backdoor/backdoor.go @@ -4,5 +4,4 @@ package backdoor -func LockedOSThread() bool // in runtime.c -func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr) +func LockedOSThread() bool // in thunk.s diff --git a/misc/cgo/test/backdoor/backdoor_gccgo.go b/misc/cgo/test/backdoor/backdoor_gccgo.go deleted file mode 100644 index 514f76ec5e..0000000000 --- a/misc/cgo/test/backdoor/backdoor_gccgo.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 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. - -// This is the gccgo version of the stub in runtime.c. - -// +build gccgo - -package backdoor - -func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr) {} diff --git a/misc/cgo/test/backdoor/runtime.c b/misc/cgo/test/backdoor/runtime.c deleted file mode 100644 index 87ee44eb6f..0000000000 --- a/misc/cgo/test/backdoor/runtime.c +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2011 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. - -// Expose some runtime functions for testing. -// Must be in a non-cgo-using package so that -// the go command compiles this file with 6c, not gcc. - -// +build gc - -typedef char bool; - -// This is what a cgo-compiled stub declaration looks like. -void -·Issue7695(struct{void *y[8*sizeof(void*)];}p) -{ - USED(p); -} diff --git a/misc/cgo/test/issue7695_test.go b/misc/cgo/test/issue7695_test.go deleted file mode 100644 index de2fc03d42..0000000000 --- a/misc/cgo/test/issue7695_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 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. - -// +build ignore -// This test depends on running C code on Go stacks. Not allowed anymore. - -// Demo of deferred C function with untrue prototype -// breaking stack copying. See golang.org/issue/7695. - -package cgotest - -import ( - "testing" - - "./backdoor" -) - -func TestIssue7695(t *testing.T) { - defer backdoor.Issue7695(1, 0, 2, 0, 0, 3, 0, 4) - recurse(100) -} - -func recurse(n int) { - var x [128]int - n += x[0] - if n > 0 { - recurse(n - 1) - } -} diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 63875aed5a..4bbcc2b971 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -614,6 +614,16 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } p.Target = p.target + // Check for C code compiled with Plan 9 C compiler. + // No longer allowed except in runtime and runtime/cgo, for now. + if len(p.CFiles) > 0 && !p.usesCgo() && (!p.Standard || p.ImportPath != "runtime") { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")), + } + return p + } + // In the absence of errors lower in the dependency tree, // check for case-insensitive collisions of import paths. if len(p.DepsErrors) == 0 { diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 13886e158b..9ae17e1054 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -157,6 +157,20 @@ fi rm -f ./testdata/err unset GOPATH +export GOPATH=$(pwd)/testdata/src +TEST disallowed C source files +export GOPATH=$(pwd)/testdata +if ./testgo build badc 2>testdata/err; then + echo 'go build badc succeeded' + ok=false +elif ! grep 'C source files not allowed' testdata/err >/dev/null; then + echo 'go test did not say C source files not allowed:' + cat testdata/err + ok=false +fi +rm -f ./testdata/err +unset GOPATH + TEST error message for syntax error in test go file says FAIL export GOPATH=$(pwd)/testdata if ./testgo test syntaxerror 2>testdata/err; then diff --git a/src/cmd/go/testdata/src/badc/x.c b/src/cmd/go/testdata/src/badc/x.c new file mode 100644 index 0000000000..f6cbf6924d --- /dev/null +++ b/src/cmd/go/testdata/src/badc/x.c @@ -0,0 +1 @@ +// C code! diff --git a/src/cmd/go/testdata/src/badc/x.go b/src/cmd/go/testdata/src/badc/x.go new file mode 100644 index 0000000000..bfa1de28bd --- /dev/null +++ b/src/cmd/go/testdata/src/badc/x.go @@ -0,0 +1 @@ +package badc diff --git a/src/net/empty.c b/src/net/empty.c deleted file mode 100644 index a515c2fe29..0000000000 --- a/src/net/empty.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 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. - -// This file is required to prevent compiler errors -// when the package built with CGO_ENABLED=0. -// Otherwise the compiler says: -// pkg/net/fd_poll_runtime.go:15: missing function body diff --git a/src/net/empty.s b/src/net/empty.s new file mode 100644 index 0000000000..a515c2fe29 --- /dev/null +++ b/src/net/empty.s @@ -0,0 +1,8 @@ +// Copyright 2013 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. + +// This file is required to prevent compiler errors +// when the package built with CGO_ENABLED=0. +// Otherwise the compiler says: +// pkg/net/fd_poll_runtime.go:15: missing function body diff --git a/src/runtime/debug/debug.c b/src/runtime/debug/debug.c deleted file mode 100644 index a7292c477b..0000000000 --- a/src/runtime/debug/debug.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2013 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. - -// Nothing to see here. -// This file exists so that the go command knows that parts of the -// package are implemented in C, so that it does not instruct the -// Go compiler to complain about extern declarations. -// The actual implementations are in package runtime. diff --git a/src/runtime/debug/debug.s b/src/runtime/debug/debug.s new file mode 100644 index 0000000000..a7292c477b --- /dev/null +++ b/src/runtime/debug/debug.s @@ -0,0 +1,9 @@ +// Copyright 2013 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. + +// Nothing to see here. +// This file exists so that the go command knows that parts of the +// package are implemented in C, so that it does not instruct the +// Go compiler to complain about extern declarations. +// The actual implementations are in package runtime. -- cgit v1.3 From 00d2f916adf2fb025406f0558ab8cac122fdf060 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 15:20:03 -0400 Subject: cmd/gc: run escape analysis always (even in -N mode) Fixes #8585. Removes some little-used code paths. LGTM=josharian R=golang-codereviews, minux, josharian CC=golang-codereviews, iant, r https://golang.org/cl/132970043 --- src/cmd/gc/gen.c | 6 +- src/cmd/gc/lex.c | 8 +- src/cmd/gc/typecheck.c | 6 - test/escape2.go | 2 + test/escape2n.go | 1494 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1503 insertions(+), 13 deletions(-) create mode 100644 test/escape2n.go (limited to 'src/cmd') diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 83c46c6504..eb9eacca8f 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -54,9 +54,6 @@ addrescapes(Node *n) if(n->class == PAUTO && n->esc == EscNever) break; - if(debug['N'] && n->esc != EscUnknown) - fatal("without escape analysis, only PAUTO's should have esc: %N", n); - switch(n->class) { case PPARAMREF: addrescapes(n->defn); @@ -91,8 +88,7 @@ addrescapes(Node *n) snprint(buf, sizeof buf, "&%S", n->sym); n->heapaddr->sym = lookup(buf); n->heapaddr->orig->sym = n->heapaddr->sym; - if(!debug['N']) - n->esc = EscHeap; + n->esc = EscHeap; if(debug['m']) print("%L: moved to heap: %N\n", n->lineno, n); curfn = oldfn; diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 6d83177477..2303b442cd 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -481,8 +481,12 @@ main(int argc, char *argv[]) } // Phase 5: Escape analysis. - if(!debug['N']) - escapes(xtop); + // Required for moving heap allocations onto stack, + // which in turn is required by the closure implementation, + // which stores the addresses of stack variables into the closure. + // If the closure does not escape, it needs to be on the stack + // or else the stack copier will not update it. + escapes(xtop); // Escape analysis moved escaped values off stack. // Move large values off stack too. diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 18d20cdd16..298920bfec 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -736,10 +736,6 @@ reswitch: l = n->left; if((t = l->type) == T) goto error; - // top&Eindir means this is &x in *&x. (or the arg to built-in print) - // n->etype means code generator flagged it as non-escaping. - if(debug['N'] && !(top & Eindir) && !n->etype) - addrescapes(n->left); n->type = ptrto(t); goto ret; @@ -2119,8 +2115,6 @@ lookdot(Node *n, Type *t, int dostrcmp) if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); - if(debug['N']) - addrescapes(n->left); n->left = nod(OADDR, n->left, N); n->left->implicit = 1; typecheck(&n->left, Etype|Erv); diff --git a/test/escape2.go b/test/escape2.go index 28251aa98b..6a46ce86ab 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -7,6 +7,8 @@ // Test, using compiler diagnostic flags, that the escape analysis is working. // Compiles but does not run. Inlining is disabled. +// escape2n.go contains all the same tests but compiles with -N. + package foo import ( diff --git a/test/escape2n.go b/test/escape2n.go new file mode 100644 index 0000000000..002a78ea50 --- /dev/null +++ b/test/escape2n.go @@ -0,0 +1,1494 @@ +// errorcheck -0 -N -m -l + +// Copyright 2010 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. + +// Test, using compiler diagnostic flags, that the escape analysis is working. +// Compiles but does not run. Inlining is disabled. +// Registerization is disabled too (-N), which should +// have no effect on escape analysis. + +package foo + +import ( + "fmt" + "unsafe" +) + +var gxx *int + +func foo1(x int) { // ERROR "moved to heap: x" + gxx = &x // ERROR "&x escapes to heap" +} + +func foo2(yy *int) { // ERROR "leaking param: yy" + gxx = yy +} + +func foo3(x int) *int { // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" +} + +type T *T + +func foo3b(t T) { // ERROR "leaking param: t" + *t = t +} + +// xx isn't going anywhere, so use of yy is ok +func foo4(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" + xx = yy +} + +// xx isn't going anywhere, so taking address of yy is ok +func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" + xx = &yy // ERROR "&yy does not escape" +} + +func foo6(xx **int, yy *int) { // ERROR "xx does not escape" "leaking param: yy" + *xx = yy +} + +func foo7(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" + **xx = *yy +} + +func foo8(xx, yy *int) int { // ERROR "xx does not escape" "yy does not escape" + xx = yy + return *xx +} + +func foo9(xx, yy *int) *int { // ERROR "leaking param: xx" "leaking param: yy" + xx = yy + return xx +} + +func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" + *xx = *yy +} + +func foo11() int { + x, y := 0, 42 + xx := &x // ERROR "&x does not escape" + yy := &y // ERROR "&y does not escape" + *xx = *yy + return x +} + +var xxx **int + +func foo12(yyy **int) { // ERROR "leaking param: yyy" + xxx = yyy +} + +// Must treat yyy as leaking because *yyy leaks, and the escape analysis +// summaries in exported metadata do not distinguish these two cases. +func foo13(yyy **int) { // ERROR "leaking param: yyy" + *xxx = *yyy +} + +func foo14(yyy **int) { // ERROR "yyy does not escape" + **xxx = **yyy +} + +func foo15(yy *int) { // ERROR "moved to heap: yy" + xxx = &yy // ERROR "&yy escapes to heap" +} + +func foo16(yy *int) { // ERROR "leaking param: yy" + *xxx = yy +} + +func foo17(yy *int) { // ERROR "yy does not escape" + **xxx = *yy +} + +func foo18(y int) { // ERROR "moved to heap: "y" + *xxx = &y // ERROR "&y escapes to heap" +} + +func foo19(y int) { + **xxx = y +} + +type Bar struct { + i int + ii *int +} + +func NewBar() *Bar { + return &Bar{42, nil} // ERROR "&Bar literal escapes to heap" +} + +func NewBarp(x *int) *Bar { // ERROR "leaking param: x" + return &Bar{42, x} // ERROR "&Bar literal escapes to heap" +} + +func NewBarp2(x *int) *Bar { // ERROR "x does not escape" + return &Bar{*x, nil} // ERROR "&Bar literal escapes to heap" +} + +func (b *Bar) NoLeak() int { // ERROR "b does not escape" + return *(b.ii) +} + +func (b *Bar) Leak() *int { // ERROR "leaking param: b" + return &b.i // ERROR "&b.i escapes to heap" +} + +func (b *Bar) AlsoNoLeak() *int { // ERROR "leaking param b content to result ~r0" + return b.ii +} + +func (b Bar) AlsoLeak() *int { // ERROR "leaking param: b" + return b.ii +} + +func (b Bar) LeaksToo() *int { // ERROR "leaking param: b" + v := 0 // ERROR "moved to heap: v" + b.ii = &v // ERROR "&v escapes" + return b.ii +} + +func (b *Bar) LeaksABit() *int { // ERROR "leaking param b content to result ~r0" + v := 0 // ERROR "moved to heap: v" + b.ii = &v // ERROR "&v escapes" + return b.ii +} + +func (b Bar) StillNoLeak() int { // ERROR "b does not escape" + v := 0 + b.ii = &v // ERROR "&v does not escape" + return b.i +} + +func goLeak(b *Bar) { // ERROR "leaking param: b" + go b.NoLeak() +} + +type Bar2 struct { + i [12]int + ii []int +} + +func NewBar2() *Bar2 { + return &Bar2{[12]int{42}, nil} // ERROR "&Bar2 literal escapes to heap" +} + +func (b *Bar2) NoLeak() int { // ERROR "b does not escape" + return b.i[0] +} + +func (b *Bar2) Leak() []int { // ERROR "leaking param: b" + return b.i[:] // ERROR "b.i escapes to heap" +} + +func (b *Bar2) AlsoNoLeak() []int { // ERROR "leaking param b content to result ~r0" + return b.ii[0:1] +} + +func (b Bar2) AgainNoLeak() [12]int { // ERROR "b does not escape" + return b.i +} + +func (b *Bar2) LeakSelf() { // ERROR "leaking param: b" + b.ii = b.i[0:4] // ERROR "b.i escapes to heap" +} + +func (b *Bar2) LeakSelf2() { // ERROR "leaking param: b" + var buf []int + buf = b.i[0:] // ERROR "b.i escapes to heap" + b.ii = buf +} + +func foo21() func() int { + x := 42 // ERROR "moved to heap: x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } +} + +func foo22() int { + x := 42 + return func() int { // ERROR "func literal does not escape" + return x + }() +} + +func foo23(x int) func() int { // ERROR "moved to heap: x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } +} + +func foo23a(x int) func() int { // ERROR "moved to heap: x" + f := func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" + } + return f +} + +func foo23b(x int) *(func() int) { // ERROR "moved to heap: x" + f := func() int { return x } // ERROR "moved to heap: f" "func literal escapes to heap" "&x escapes to heap" + return &f // ERROR "&f escapes to heap" +} + +func foo24(x int) int { + return func() int { // ERROR "func literal does not escape" + return x + }() +} + +var x *int + +func fooleak(xx *int) int { // ERROR "leaking param: xx" + x = xx + return *x +} + +func foonoleak(xx *int) int { // ERROR "xx does not escape" + return *x + *xx +} + +func foo31(x int) int { // ERROR "moved to heap: x" + return fooleak(&x) // ERROR "&x escapes to heap" +} + +func foo32(x int) int { + return foonoleak(&x) // ERROR "&x does not escape" +} + +type Foo struct { + xx *int + x int +} + +var F Foo +var pf *Foo + +func (f *Foo) fooleak() { // ERROR "leaking param: f" + pf = f +} + +func (f *Foo) foonoleak() { // ERROR "f does not escape" + F.x = f.x +} + +func (f *Foo) Leak() { // ERROR "leaking param: f" + f.fooleak() +} + +func (f *Foo) NoLeak() { // ERROR "f does not escape" + f.foonoleak() +} + +func foo41(x int) { // ERROR "moved to heap: x" + F.xx = &x // ERROR "&x escapes to heap" +} + +func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: x" + f.xx = &x // ERROR "&x escapes to heap" +} + +func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: x" + f.xx = &x // ERROR "&x escapes to heap" +} + +func foo44(yy *int) { // ERROR "leaking param: yy" + F.xx = yy +} + +func (f *Foo) foo45() { // ERROR "f does not escape" + F.x = f.x +} + +// See foo13 above for explanation of why f leaks. +func (f *Foo) foo46() { // ERROR "leaking param: f" + F.xx = f.xx +} + +func (f *Foo) foo47() { // ERROR "leaking param: f" + f.xx = &f.x // ERROR "&f.x escapes to heap" +} + +var ptrSlice []*int + +func foo50(i *int) { // ERROR "leaking param: i" + ptrSlice[0] = i +} + +var ptrMap map[*int]*int + +func foo51(i *int) { // ERROR "leaking param: i" + ptrMap[i] = i +} + +func indaddr1(x int) *int { // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" +} + +func indaddr2(x *int) *int { // ERROR "leaking param: x" + return *&x // ERROR "&x does not escape" +} + +func indaddr3(x *int32) *int { // ERROR "leaking param: x" + return *(**int)(unsafe.Pointer(&x)) // ERROR "&x does not escape" +} + +// From package math: + +func Float32bits(f float32) uint32 { + return *(*uint32)(unsafe.Pointer(&f)) // ERROR "&f does not escape" +} + +func Float32frombits(b uint32) float32 { + return *(*float32)(unsafe.Pointer(&b)) // ERROR "&b does not escape" +} + +func Float64bits(f float64) uint64 { + return *(*uint64)(unsafe.Pointer(&f)) // ERROR "&f does not escape" +} + +func Float64frombits(b uint64) float64 { + return *(*float64)(unsafe.Pointer(&b)) // ERROR "&b does not escape" +} + +// contrast with +func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: f" + return (*uint64)(unsafe.Pointer(&f)) // ERROR "&f escapes to heap" +} + +func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: f" + return (*uint64)(unsafe.Pointer(f)) +} + +func typesw(i interface{}) *int { // ERROR "leaking param: i" + switch val := i.(type) { + case *int: + return val + case *int8: + v := int(*val) // ERROR "moved to heap: v" + return &v // ERROR "&v escapes to heap" + } + return nil +} + +func exprsw(i *int) *int { // ERROR "leaking param: i" + switch j := i; *j + 110 { + case 12: + return j + case 42: + return nil + } + return nil + +} + +// assigning to an array element is like assigning to the array +func foo60(i *int) *int { // ERROR "leaking param: i" + var a [12]*int + a[0] = i + return a[1] +} + +func foo60a(i *int) *int { // ERROR "i does not escape" + var a [12]*int + a[0] = i + return nil +} + +// assigning to a struct field is like assigning to the struct +func foo61(i *int) *int { // ERROR "leaking param: i" + type S struct { + a, b *int + } + var s S + s.a = i + return s.b +} + +func foo61a(i *int) *int { // ERROR "i does not escape" + type S struct { + a, b *int + } + var s S + s.a = i + return nil +} + +// assigning to a struct field is like assigning to the struct but +// here this subtlety is lost, since s.a counts as an assignment to a +// track-losing dereference. +func foo62(i *int) *int { // ERROR "leaking param: i" + type S struct { + a, b *int + } + s := new(S) // ERROR "new[(]S[)] does not escape" + s.a = i + return nil // s.b +} + +type M interface { + M() +} + +func foo63(m M) { // ERROR "m does not escape" +} + +func foo64(m M) { // ERROR "leaking param: m" + m.M() +} + +func foo64b(m M) { // ERROR "leaking param: m" + defer m.M() +} + +type MV int + +func (MV) M() {} + +func foo65() { + var mv MV + foo63(&mv) // ERROR "&mv does not escape" +} + +func foo66() { + var mv MV // ERROR "moved to heap: mv" + foo64(&mv) // ERROR "&mv escapes to heap" +} + +func foo67() { + var mv MV + foo63(mv) +} + +func foo68() { + var mv MV + foo64(mv) // escapes but it's an int so irrelevant +} + +func foo69(m M) { // ERROR "leaking param: m" + foo64(m) +} + +func foo70(mv1 *MV, m M) { // ERROR "leaking param: mv1" "leaking param: m" + m = mv1 + foo64(m) +} + +func foo71(x *int) []*int { // ERROR "leaking param: x" + var y []*int + y = append(y, x) + return y +} + +func foo71a(x int) []*int { // ERROR "moved to heap: x" + var y []*int + y = append(y, &x) // ERROR "&x escapes to heap" + return y +} + +func foo72() { + var x int + var y [1]*int + y[0] = &x // ERROR "&x does not escape" +} + +func foo72aa() [10]*int { + var x int // ERROR "moved to heap: x" + var y [10]*int + y[0] = &x // ERROR "&x escapes to heap" + return y +} + +func foo72a() { + var y [10]*int + for i := 0; i < 10; i++ { + // escapes its scope + x := i // ERROR "moved to heap: x" + y[i] = &x // ERROR "&x escapes to heap" + } + return +} + +func foo72b() [10]*int { + var y [10]*int + for i := 0; i < 10; i++ { + x := i // ERROR "moved to heap: x" + y[i] = &x // ERROR "&x escapes to heap" + } + return y +} + +// issue 2145 +func foo73() { + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for _, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + defer func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + }() + } +} + +func foo74() { + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for _, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + fn := func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + } + defer fn() + } +} + +// issue 3975 +func foo74b() { + var array [3]func() + s := []int{3, 2, 1} // ERROR "\[\]int literal does not escape" + for i, v := range s { + vv := v // ERROR "moved to heap: vv" + // actually just escapes its scope + array[i] = func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" + } + } +} + +func myprint(y *int, x ...interface{}) *int { // ERROR "x does not escape" "leaking param: y" + return y +} + +func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: x" + return &x[0] // ERROR "&x.0. escapes to heap" +} + +func foo75(z *int) { // ERROR "z does not escape" + myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75a(z *int) { // ERROR "z does not escape" + myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75esc(z *int) { // ERROR "leaking param: z" + gxx = myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo75aesc(z *int) { // ERROR "z does not escape" + var ppi **interface{} // assignments to pointer dereferences lose track + *ppi = myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" +} + +func foo76(z *int) { // ERROR "leaking param: z" + myprint(nil, z) // ERROR "[.][.][.] argument does not escape" +} + +func foo76a(z *int) { // ERROR "leaking param: z" + myprint1(nil, z) // ERROR "[.][.][.] argument does not escape" +} + +func foo76b() { + myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76c() { + myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76d() { + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76e() { + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" +} + +func foo76f() { + for { + // TODO: This one really only escapes its scope, but we don't distinguish yet. + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" + } +} + +func foo76g() { + for { + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" + } +} + +func foo77(z []interface{}) { // ERROR "z does not escape" + myprint(nil, z...) // z does not escape +} + +func foo77a(z []interface{}) { // ERROR "z does not escape" + myprint1(nil, z...) +} + +func foo77b(z []interface{}) { // ERROR "leaking param: z" + var ppi **interface{} + *ppi = myprint1(nil, z...) +} + +func foo78(z int) *int { // ERROR "moved to heap: z" + return &z // ERROR "&z escapes to heap" +} + +func foo78a(z int) *int { // ERROR "moved to heap: z" + y := &z // ERROR "&z escapes to heap" + x := &y // ERROR "&y does not escape" + return *x // really return y +} + +func foo79() *int { + return new(int) // ERROR "new[(]int[)] escapes to heap" +} + +func foo80() *int { + var z *int + for { + // Really just escapes its scope but we don't distinguish + z = new(int) // ERROR "new[(]int[)] escapes to heap" + } + _ = z + return nil +} + +func foo81() *int { + for { + z := new(int) // ERROR "new[(]int[)] does not escape" + _ = z + } + return nil +} + +func tee(p *int) (x, y *int) { return p, p } // ERROR "leaking param" + +func noop(x, y *int) {} // ERROR "does not escape" + +func foo82() { + var x, y, z int // ERROR "moved to heap" + go noop(tee(&z)) // ERROR "&z escapes to heap" + go noop(&x, &y) // ERROR "escapes to heap" + for { + var u, v, w int // ERROR "moved to heap" + defer noop(tee(&u)) // ERROR "&u escapes to heap" + defer noop(&v, &w) // ERROR "escapes to heap" + } +} + +type Fooer interface { + Foo() +} + +type LimitedFooer struct { + Fooer + N int64 +} + +func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: r" + return &LimitedFooer{r, n} // ERROR "&LimitedFooer literal escapes to heap" +} + +func foo90(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{nil: x} // ERROR "map\[\*int\]\*int literal escapes to heap" +} + +func foo91(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{x: nil} // ERROR "map\[\*int\]\*int literal escapes to heap" +} + +func foo92(x *int) [2]*int { // ERROR "leaking param: x" + return [2]*int{x, nil} +} + +// does not leak c +func foo93(c chan *int) *int { // ERROR "c does not escape" + for v := range c { + return v + } + return nil +} + +// does not leak m +func foo94(m map[*int]*int, b bool) *int { // ERROR "m does not escape" + for k, v := range m { + if b { + return k + } + return v + } + return nil +} + +// does leak x +func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape" "leaking param: x" + m[x] = x +} + +// does not leak m +func foo96(m []*int) *int { // ERROR "m does not escape" + return m[0] +} + +// does leak m +func foo97(m [1]*int) *int { // ERROR "leaking param: m" + return m[0] +} + +// does not leak m +func foo98(m map[int]*int) *int { // ERROR "m does not escape" + return m[0] +} + +// does leak m +func foo99(m *[1]*int) []*int { // ERROR "leaking param: m" + return m[:] +} + +// does not leak m +func foo100(m []*int) *int { // ERROR "m does not escape" + for _, v := range m { + return v + } + return nil +} + +// does leak m +func foo101(m [1]*int) *int { // ERROR "leaking param: m" + for _, v := range m { + return v + } + return nil +} + +// does not leak m +func foo101a(m [1]*int) *int { // ERROR "m does not escape" + for i := range m { // ERROR "moved to heap: i" + return &i // ERROR "&i escapes to heap" + } + return nil +} + +// does leak x +func foo102(m []*int, x *int) { // ERROR "m does not escape" "leaking param: x" + m[0] = x +} + +// does not leak x +func foo103(m [1]*int, x *int) { // ERROR "m does not escape" "x does not escape" + m[0] = x +} + +var y []*int + +// does not leak x +func foo104(x []*int) { // ERROR "x does not escape" + copy(y, x) +} + +// does not leak x +func foo105(x []*int) { // ERROR "x does not escape" + _ = append(y, x...) +} + +// does leak x +func foo106(x *int) { // ERROR "leaking param: x" + _ = append(y, x) +} + +func foo107(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{x: nil} // ERROR "map.* literal escapes to heap" +} + +func foo108(x *int) map[*int]*int { // ERROR "leaking param: x" + return map[*int]*int{nil: x} // ERROR "map.* literal escapes to heap" +} + +func foo109(x *int) *int { // ERROR "leaking param: x" + m := map[*int]*int{x: nil} // ERROR "map.* literal does not escape" + for k, _ := range m { + return k + } + return nil +} + +func foo110(x *int) *int { // ERROR "leaking param: x" + m := map[*int]*int{nil: x} // ERROR "map.* literal does not escape" + return m[nil] +} + +func foo111(x *int) *int { // ERROR "leaking param: x" + m := []*int{x} // ERROR "\[\]\*int literal does not escape" + return m[0] +} + +func foo112(x *int) *int { // ERROR "leaking param: x" + m := [1]*int{x} + return m[0] +} + +func foo113(x *int) *int { // ERROR "leaking param: x" + m := Bar{ii: x} + return m.ii +} + +func foo114(x *int) *int { // ERROR "leaking param: x" + m := &Bar{ii: x} // ERROR "&Bar literal does not escape" + return m.ii +} + +func foo115(x *int) *int { // ERROR "leaking param: x" + return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1)) +} + +func foo116(b bool) *int { + if b { + x := 1 // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" + } else { + y := 1 // ERROR "moved to heap: y" + return &y // ERROR "&y escapes to heap" + } + return nil +} + +func foo117(unknown func(interface{})) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: x" + unknown(&x) // ERROR "&x escapes to heap" +} + +func foo118(unknown func(*int)) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: x" + unknown(&x) // ERROR "&x escapes to heap" +} + +func external(*int) + +func foo119(x *int) { // ERROR "leaking param: x" + external(x) +} + +func foo120() { + // formerly exponential time analysis +L1: +L2: +L3: +L4: +L5: +L6: +L7: +L8: +L9: +L10: +L11: +L12: +L13: +L14: +L15: +L16: +L17: +L18: +L19: +L20: +L21: +L22: +L23: +L24: +L25: +L26: +L27: +L28: +L29: +L30: +L31: +L32: +L33: +L34: +L35: +L36: +L37: +L38: +L39: +L40: +L41: +L42: +L43: +L44: +L45: +L46: +L47: +L48: +L49: +L50: +L51: +L52: +L53: +L54: +L55: +L56: +L57: +L58: +L59: +L60: +L61: +L62: +L63: +L64: +L65: +L66: +L67: +L68: +L69: +L70: +L71: +L72: +L73: +L74: +L75: +L76: +L77: +L78: +L79: +L80: +L81: +L82: +L83: +L84: +L85: +L86: +L87: +L88: +L89: +L90: +L91: +L92: +L93: +L94: +L95: +L96: +L97: +L98: +L99: +L100: + // use the labels to silence compiler errors + goto L1 + goto L2 + goto L3 + goto L4 + goto L5 + goto L6 + goto L7 + goto L8 + goto L9 + goto L10 + goto L11 + goto L12 + goto L13 + goto L14 + goto L15 + goto L16 + goto L17 + goto L18 + goto L19 + goto L20 + goto L21 + goto L22 + goto L23 + goto L24 + goto L25 + goto L26 + goto L27 + goto L28 + goto L29 + goto L30 + goto L31 + goto L32 + goto L33 + goto L34 + goto L35 + goto L36 + goto L37 + goto L38 + goto L39 + goto L40 + goto L41 + goto L42 + goto L43 + goto L44 + goto L45 + goto L46 + goto L47 + goto L48 + goto L49 + goto L50 + goto L51 + goto L52 + goto L53 + goto L54 + goto L55 + goto L56 + goto L57 + goto L58 + goto L59 + goto L60 + goto L61 + goto L62 + goto L63 + goto L64 + goto L65 + goto L66 + goto L67 + goto L68 + goto L69 + goto L70 + goto L71 + goto L72 + goto L73 + goto L74 + goto L75 + goto L76 + goto L77 + goto L78 + goto L79 + goto L80 + goto L81 + goto L82 + goto L83 + goto L84 + goto L85 + goto L86 + goto L87 + goto L88 + goto L89 + goto L90 + goto L91 + goto L92 + goto L93 + goto L94 + goto L95 + goto L96 + goto L97 + goto L98 + goto L99 + goto L100 +} + +func foo121() { + for i := 0; i < 10; i++ { + defer myprint(nil, i) // ERROR "[.][.][.] argument escapes to heap" + go myprint(nil, i) // ERROR "[.][.][.] argument escapes to heap" + } +} + +// same as foo121 but check across import +func foo121b() { + for i := 0; i < 10; i++ { + defer fmt.Printf("%d", i) // ERROR "[.][.][.] argument escapes to heap" + go fmt.Printf("%d", i) // ERROR "[.][.][.] argument escapes to heap" + } +} + +// a harmless forward jump +func foo122() { + var i *int + + goto L1 +L1: + i = new(int) // ERROR "new.int. does not escape" + _ = i +} + +// a backward jump, increases loopdepth +func foo123() { + var i *int + +L1: + i = new(int) // ERROR "new.int. escapes to heap" + + goto L1 + _ = i +} + +func foo124(x **int) { // ERROR "x does not escape" + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes" + func() { // ERROR "func literal does not escape" + *x = p // ERROR "leaking closure reference p" + }() +} + +func foo125(ch chan *int) { // ERROR "does not escape" + var i int // ERROR "moved to heap" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + ch <- p // ERROR "leaking closure reference p" + }() +} + +func foo126() { + var px *int // loopdepth 0 + for { + // loopdepth 1 + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" + }() + } + _ = px +} + +var px *int + +func foo127() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + q := p + px = q +} + +func foo128() { + var i int + p := &i // ERROR "&i does not escape" + q := p + _ = q +} + +func foo129() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + q := p // ERROR "leaking closure reference p" + func() { // ERROR "func literal does not escape" + r := q // ERROR "leaking closure reference q" + px = r + }() + }() +} + +func foo130() { + for { + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() + } +} + +func foo131() { + var i int // ERROR "moved to heap" + func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo132() { + var i int // ERROR "moved to heap" + go func() { // ERROR "func literal escapes to heap" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo133() { + var i int // ERROR "moved to heap" + defer func() { // ERROR "func literal does not escape" + px = &i // ERROR "&i escapes" "leaking closure reference i" + }() +} + +func foo134() { + var i int + p := &i // ERROR "&i does not escape" + func() { // ERROR "func literal does not escape" + q := p + func() { // ERROR "func literal does not escape" + r := q + _ = r + }() + }() +} + +func foo135() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" "moved to heap: p" + go func() { // ERROR "func literal escapes to heap" + q := p // ERROR "&p escapes to heap" + func() { // ERROR "func literal does not escape" + r := q + _ = r + }() + }() +} + +func foo136() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" "moved to heap: p" + go func() { // ERROR "func literal escapes to heap" + q := p // ERROR "&p escapes to heap" "leaking closure reference p" + func() { // ERROR "func literal does not escape" + r := q // ERROR "leaking closure reference q" + px = r + }() + }() +} + +func foo137() { + var i int // ERROR "moved to heap: i" + p := &i // ERROR "&i escapes to heap" + func() { // ERROR "func literal does not escape" + q := p // ERROR "leaking closure reference p" "moved to heap: q" + go func() { // ERROR "func literal escapes to heap" + r := q // ERROR "&q escapes to heap" + _ = r + }() + }() +} + +func foo138() *byte { + type T struct { + x [1]byte + } + t := new(T) // ERROR "new.T. escapes to heap" + return &t.x[0] // ERROR "&t.x.0. escapes to heap" +} + +func foo139() *byte { + type T struct { + x struct { + y byte + } + } + t := new(T) // ERROR "new.T. escapes to heap" + return &t.x.y // ERROR "&t.x.y escapes to heap" +} + +// issue 4751 +func foo140() interface{} { + type T struct { + X string + } + type U struct { + X string + T *T + } + t := &T{} // ERROR "&T literal escapes to heap" + return U{ + X: t.X, + T: t, + } +} + +//go:noescape + +func F1([]byte) + +func F2([]byte) + +//go:noescape + +func F3(x []byte) // ERROR "F3 x does not escape" + +func F4(x []byte) + +func G() { + var buf1 [10]byte + F1(buf1[:]) // ERROR "buf1 does not escape" + + var buf2 [10]byte // ERROR "moved to heap: buf2" + F2(buf2[:]) // ERROR "buf2 escapes to heap" + + var buf3 [10]byte + F3(buf3[:]) // ERROR "buf3 does not escape" + + var buf4 [10]byte // ERROR "moved to heap: buf4" + F4(buf4[:]) // ERROR "buf4 escapes to heap" +} + +type Tm struct { + x int +} + +func (t *Tm) M() { // ERROR "t does not escape" +} + +func foo141() { + var f func() + + t := new(Tm) // ERROR "escapes to heap" + f = t.M // ERROR "t.M does not escape" + _ = f +} + +var gf func() + +func foo142() { + t := new(Tm) // ERROR "escapes to heap" + gf = t.M // ERROR "t.M escapes to heap" +} + +// issue 3888. +func foo143() { + for i := 0; i < 1000; i++ { + func() { // ERROR "func literal does not escape" + for i := 0; i < 1; i++ { + var t Tm + t.M() // ERROR "t does not escape" + } + }() + } +} + +// issue 5773 +// Check that annotations take effect regardless of whether they +// are before or after the use in the source code. + +//go:noescape + +func foo144a(*int) + +func foo144() { + var x int + foo144a(&x) // ERROR "&x does not escape" + var y int + foo144b(&y) // ERROR "&y does not escape" +} + +//go:noescape + +func foo144b(*int) + +// issue 7313: for loop init should not be treated as "in loop" + +type List struct { + Next *List +} + +func foo145(l List) { // ERROR "l does not escape" + var p *List + for p = &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } +} + +func foo146(l List) { // ERROR "l does not escape" + var p *List + p = &l // ERROR "&l does not escape" + for ; p.Next != nil; p = p.Next { + } +} + +func foo147(l List) { // ERROR "l does not escape" + var p *List + p = &l // ERROR "&l does not escape" + for p.Next != nil { + p = p.Next + } +} + +func foo148(l List) { // ERROR " l does not escape" + for p := &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } +} + +// related: address of variable should have depth of variable, not of loop + +func foo149(l List) { // ERROR " l does not escape" + var p *List + for { + for p = &l; p.Next != nil; p = p.Next { // ERROR "&l does not escape" + } + } +} + +// issue 7934: missed ... if element type had no pointers + +var save150 []byte + +func foo150(x ...byte) { // ERROR "leaking param: x" + save150 = x +} + +func bar150() { + foo150(1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" +} + +// issue 7931: bad handling of slice of array + +var save151 *int + +func foo151(x *int) { // ERROR "leaking param: x" + save151 = x +} + +func bar151() { + var a [64]int // ERROR "moved to heap: a" + a[4] = 101 + foo151(&(&a)[4:8][0]) // ERROR "&\(&a\)\[4:8\]\[0\] escapes to heap" "&a escapes to heap" +} + +func bar151b() { + var a [10]int // ERROR "moved to heap: a" + b := a[:] // ERROR "a escapes to heap" + foo151(&b[4:8][0]) // ERROR "&b\[4:8\]\[0\] escapes to heap" +} + +func bar151c() { + var a [64]int // ERROR "moved to heap: a" + a[4] = 101 + foo151(&(&a)[4:8:8][0]) // ERROR "&\(&a\)\[4:8:8\]\[0\] escapes to heap" "&a escapes to heap" +} + +func bar151d() { + var a [10]int // ERROR "moved to heap: a" + b := a[:] // ERROR "a escapes to heap" + foo151(&b[4:8:8][0]) // ERROR "&b\[4:8:8\]\[0\] escapes to heap" +} + +// issue 8120 + +type U struct { + s *string +} + +func (u *U) String() *string { // ERROR "leaking param u content to result ~r0" + return u.s +} + +type V struct { + s *string +} + +func NewV(u U) *V { // ERROR "leaking param: u" + return &V{u.String()} // ERROR "&V literal escapes to heap" "u does not escape" +} + +func foo152() { + a := "a" // ERROR "moved to heap: a" + u := U{&a} // ERROR "&a escapes to heap" + v := NewV(u) + println(v) +} + +// issue 8176 - &x in type switch body not marked as escaping + +func foo153(v interface{}) *int { // ERROR "leaking param: v" + switch x := v.(type) { + case int: // ERROR "moved to heap: x" + return &x // ERROR "&x escapes to heap" + } + panic(0) +} + +// issue 8185 - &result escaping into result + +func f() (x int, y *int) { // ERROR "moved to heap: x" + y = &x // ERROR "&x escapes to heap" + return +} + +func g() (x interface{}) { // ERROR "moved to heap: x" + x = &x // ERROR "&x escapes to heap" + return +} -- cgit v1.3 From 43c4287b25da53b2e8cb0de64d40689c56eb42bd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:53:34 -0400 Subject: cmd/gc: fix import of package with var func returning _ Fixes #8280. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/146240043 --- src/cmd/gc/fmt.c | 15 +++++---------- test/fixedbugs/issue8280.dir/a.go | 3 +++ test/fixedbugs/issue8280.dir/b.go | 5 +++++ test/fixedbugs/issue8280.go | 9 +++++++++ 4 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 test/fixedbugs/issue8280.dir/a.go create mode 100644 test/fixedbugs/issue8280.dir/b.go create mode 100644 test/fixedbugs/issue8280.go (limited to 'src/cmd') diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 98556a658f..f67757449b 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -1108,16 +1108,11 @@ exprfmt(Fmt *f, Node *n, int prec) case ONAME: // Special case: name used as local variable in export. - switch(n->class&~PHEAP){ - case PAUTO: - case PPARAM: - case PPARAMOUT: - // _ becomes ~b%d internally; print as _ for export - if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b') - return fmtprint(f, "_"); - if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0) - return fmtprint(f, "%S·%d", n->sym, n->vargen); - } + // _ becomes ~b%d internally; print as _ for export + if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b') + return fmtprint(f, "_"); + if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0) + return fmtprint(f, "%S·%d", n->sym, n->vargen); // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. diff --git a/test/fixedbugs/issue8280.dir/a.go b/test/fixedbugs/issue8280.dir/a.go new file mode 100644 index 0000000000..588536e79a --- /dev/null +++ b/test/fixedbugs/issue8280.dir/a.go @@ -0,0 +1,3 @@ +package a + +var Bar = func() (_ int) { return 0 } diff --git a/test/fixedbugs/issue8280.dir/b.go b/test/fixedbugs/issue8280.dir/b.go new file mode 100644 index 0000000000..c46c554588 --- /dev/null +++ b/test/fixedbugs/issue8280.dir/b.go @@ -0,0 +1,5 @@ +package b + +import "./a" + +var foo = a.Bar diff --git a/test/fixedbugs/issue8280.go b/test/fixedbugs/issue8280.go new file mode 100644 index 0000000000..91256c852d --- /dev/null +++ b/test/fixedbugs/issue8280.go @@ -0,0 +1,9 @@ +// compiledir + +// Copyright 2014 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. + +// Issue 8280: cannot import package exporting a func var returning a result named _ + +package ignored -- cgit v1.3 From 193daab9889708f7a20ff46efe0fa4b2bf0468d3 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 16:55:26 -0400 Subject: cmd/cc, cmd/ld, runtime: disallow conservative data/bss objects In linker, refuse to write conservative (array of pointers) as the garbage collection type for any variable in the data/bss GC program. In the linker, attach the Go type to an already-read C declaration during dedup. This gives us Go types for C globals for free as long as the cmd/dist-generated Go code contains the declaration. (Most runtime C declarations have a corresponding Go declaration. Both are bss declarations and so the linker dedups them.) In cmd/dist, add a few more C files to the auto-Go-declaration list in order to get Go type information for the C declarations into the linker. In C compiler, mark all non-pointer-containing global declarations and all string data as NOPTR. This allows them to exist in C files without any corresponding Go declaration. Count C function pointers as "non-pointer-containing", since we have no heap-allocated C functions. In runtime, add NOPTR to the remaining pointer-containing declarations, none of which refer to Go heap objects. In runtime, also move os.Args and syscall.envs data into runtime-owned variables. Otherwise, in programs that do not import os or syscall, the runtime variables named os.Args and syscall.envs will be missing type information. I believe that this CL eliminates the final source of conservative GC scanning in non-SWIG Go programs, and therefore... Fixes #909. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/149770043 --- src/cmd/cc/dcl.c | 9 ++++++--- src/cmd/cc/lex.c | 2 ++ src/cmd/cgo/out.go | 13 +++++++++++++ src/cmd/dist/buildruntime.c | 2 ++ src/cmd/ld/data.c | 1 + src/liblink/objfile.c | 6 ++++-- src/os/proc.go | 6 ++++++ src/runtime/asm_386.s | 2 -- src/runtime/asm_amd64.s | 2 -- src/runtime/asm_amd64p32.s | 2 -- src/runtime/heapdump.c | 3 +++ src/runtime/malloc.c | 23 ----------------------- src/runtime/malloc.h | 2 -- src/runtime/mcache.c | 2 +- src/runtime/mgc0.c | 5 ++--- src/runtime/os_windows.c | 6 +++--- src/runtime/proc.c | 30 +++++++++--------------------- src/runtime/proc.go | 8 ++++++++ src/runtime/runtime.c | 25 ++++++++++++++++--------- src/runtime/runtime.go | 11 +++++++++++ src/runtime/signals_darwin.h | 3 +++ src/runtime/signals_dragonfly.h | 3 +++ src/runtime/signals_freebsd.h | 3 +++ src/runtime/signals_linux.h | 3 +++ src/runtime/signals_nacl.h | 3 +++ src/runtime/signals_netbsd.h | 3 +++ src/runtime/signals_openbsd.h | 3 +++ src/runtime/signals_plan9.h | 3 +++ src/runtime/signals_solaris.h | 3 +++ src/runtime/stack.c | 4 ++-- src/runtime/thunk.s | 8 +++++++- src/syscall/env_unix.go | 4 +++- 32 files changed, 126 insertions(+), 77 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index 292717d688..117508fd6d 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -30,6 +30,9 @@ #include #include "cc.h" +#include "../ld/textflag.h" + +static int haspointers(Type*); Node* dodecl(void (*f)(int,Type*,Sym*), int c, Type *t, Node *n) @@ -123,7 +126,8 @@ loop: if(dataflag) { s->dataflag = dataflag; dataflag = 0; - } + } else if(s->type != T && !haspointers(s->type)) + s->dataflag = NOPTR; firstbit = 0; n->sym = s; n->type = s->type; @@ -568,9 +572,8 @@ haspointers(Type *t) return 0; case TARRAY: return haspointers(t->link); - case TFUNC: case TIND: - return 1; + return t->link->etype != TFUNC; default: return 0; } diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 55fc36b1e0..7c9f718c09 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -31,6 +31,7 @@ #include #include "cc.h" #include "y.tab.h" +#include "../ld/textflag.h" #ifndef CPP #define CPP "cpp" @@ -1317,6 +1318,7 @@ cinit(void) t->width = 0; symstring = slookup(".string"); symstring->class = CSTATIC; + symstring->dataflag = NOPTR; symstring->type = t; t = typ(TARRAY, types[TCHAR]); diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 6586531ada..2d14f766fc 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -129,6 +129,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) fmt.Fprintf(&gccgoInit, "\t%s = %s%s;\n", n.Mangle, amp, n.C) } else { + fmt.Fprintf(fc, "#pragma dataflag NOPTR /* C pointer, not heap pointer */ \n") fmt.Fprintf(fc, "void *·%s = %s%s;\n", n.Mangle, amp, n.C) } fmt.Fprintf(fc, "\n") @@ -397,6 +398,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { // C wrapper calls into gcc, passing a pointer to the argument frame. fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname) fmt.Fprintf(fc, "void %s(void*);\n", cname) + fmt.Fprintf(fc, "#pragma dataflag NOPTR\n") fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname) nret := 0 @@ -1151,20 +1153,31 @@ void *_CMalloc(size_t); const cProlog = ` #include "runtime.h" #include "cgocall.h" +#include "textflag.h" +#pragma dataflag NOPTR static void *cgocall_errno = runtime·cgocall_errno; +#pragma dataflag NOPTR void *·_cgo_runtime_cgocall_errno = &cgocall_errno; +#pragma dataflag NOPTR static void *runtime_gostring = runtime·gostring; +#pragma dataflag NOPTR void *·_cgo_runtime_gostring = &runtime_gostring; +#pragma dataflag NOPTR static void *runtime_gostringn = runtime·gostringn; +#pragma dataflag NOPTR void *·_cgo_runtime_gostringn = &runtime_gostringn; +#pragma dataflag NOPTR static void *runtime_gobytes = runtime·gobytes; +#pragma dataflag NOPTR void *·_cgo_runtime_gobytes = &runtime_gobytes; +#pragma dataflag NOPTR static void *runtime_cmalloc = runtime·cmalloc; +#pragma dataflag NOPTR void *·_cgo_runtime_cmalloc = &runtime_cmalloc; void ·_Cerrno(void*, int32); diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index 1257d5b811..bb774e05fc 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -330,9 +330,11 @@ mkzsys(char *dir, char *file) static char *runtimedefs[] = { "defs.c", "malloc.c", + "mcache.c", "mgc0.c", "proc.c", "parfor.c", + "stack.c", }; // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 71624c3304..9d224d9eb9 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -818,6 +818,7 @@ proggenaddsym(ProgGen *g, LSym *s) if(s->gotype == nil && s->size >= PtrSize) { // conservative scan + diag("missing Go type information for global symbol: %s", s->name); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); diff --git a/src/liblink/objfile.c b/src/liblink/objfile.c index 9b1e1b7a8f..15d602df92 100644 --- a/src/liblink/objfile.c +++ b/src/liblink/objfile.c @@ -550,7 +550,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) static int ndup; char *name; Reloc *r; - LSym *s, *dup; + LSym *s, *dup, *typ; Pcln *pc; Auto *a; @@ -586,7 +586,9 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn) s->type = t; if(s->size < size) s->size = size; - s->gotype = rdsym(ctxt, f, pkg); + typ = rdsym(ctxt, f, pkg); + if(typ != nil) // if bss sym defined multiple times, take type from any one def + s->gotype = typ; rddata(f, &s->p, &s->np); s->maxp = s->np; n = rdint(f); diff --git a/src/os/proc.go b/src/os/proc.go index 38c436ec54..b63c85ad90 100644 --- a/src/os/proc.go +++ b/src/os/proc.go @@ -11,6 +11,12 @@ import "syscall" // Args hold the command-line arguments, starting with the program name. var Args []string +func init() { + Args = runtime_args() +} + +func runtime_args() []string // in package runtime + // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 2961f10f2a..846a214d55 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -903,8 +903,6 @@ TEXT runtime·emptyfunc(SB),0,$0-0 TEXT runtime·abort(SB),NOSPLIT,$0-0 INT $0x3 -GLOBL runtime·tls0(SB), $32 - // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-16 MOVL p+0(FP), AX // ptr to data diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 44159bb57e..7304d79a2f 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -871,8 +871,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // hash function using AES hardware instructions TEXT runtime·aeshash(SB),NOSPLIT,$0-32 MOVQ p+0(FP), AX // ptr to data diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s index bbbd886a53..13a1642568 100644 --- a/src/runtime/asm_amd64p32.s +++ b/src/runtime/asm_amd64p32.s @@ -674,8 +674,6 @@ TEXT runtime·gocputicks(SB),NOSPLIT,$0-8 MOVQ AX, ret+0(FP) RET -GLOBL runtime·tls0(SB), $64 - // hash function using AES hardware instructions // For now, our one amd64p32 system (NaCl) does not // support using AES instructions, so have not bothered to diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c index 75897c3d35..54b9666b55 100644 --- a/src/runtime/heapdump.c +++ b/src/runtime/heapdump.c @@ -59,6 +59,8 @@ static BitVector makeheapobjbv(byte *p, uintptr size); // fd to write the dump to. static uintptr dumpfd; + +#pragma dataflag NOPTR /* tmpbuf not a heap pointer at least */ static byte *tmpbuf; static uintptr tmpbufsize; @@ -109,6 +111,7 @@ typedef struct TypeCacheBucket TypeCacheBucket; struct TypeCacheBucket { Type *t[TypeCacheAssoc]; }; +#pragma dataflag NOPTR /* only initialized and used while world is stopped */ static TypeCacheBucket typecache[TypeCacheBuckets]; // dump a uint64 in a varint format parseable by encoding/binary diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c index 60d20a992d..b79c30b720 100644 --- a/src/runtime/malloc.c +++ b/src/runtime/malloc.c @@ -329,29 +329,6 @@ runtime·MHeap_SysAlloc(MHeap *h, uintptr n) return p; } -// Runtime stubs. - -static void* -cnew(Type *typ, intgo n) -{ - if(n < 0 || (typ->size > 0 && n > MaxMem/typ->size)) - runtime·throw("runtime: allocation size out of range"); - return runtime·mallocgc(typ->size*n, typ, typ->kind&KindNoPointers ? FlagNoScan : 0); -} - -// same as runtime·new, but callable from C -void* -runtime·cnew(Type *typ) -{ - return cnew(typ, 1); -} - -void* -runtime·cnewarray(Type *typ, intgo n) -{ - return cnew(typ, n); -} - void runtime·setFinalizer_m(void) { diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h index 410a007173..b90f1baf29 100644 --- a/src/runtime/malloc.h +++ b/src/runtime/malloc.h @@ -526,8 +526,6 @@ uintptr runtime·sweepone(void); void runtime·markspan(void *v, uintptr size, uintptr n, bool leftover); void runtime·unmarkspan(void *v, uintptr size); void runtime·purgecachedstats(MCache*); -void* runtime·cnew(Type*); -void* runtime·cnewarray(Type*, intgo); void runtime·tracealloc(void*, uintptr, Type*); void runtime·tracefree(void*, uintptr); void runtime·tracegc(void); diff --git a/src/runtime/mcache.c b/src/runtime/mcache.c index 17ea5d2e26..5fdbe32667 100644 --- a/src/runtime/mcache.c +++ b/src/runtime/mcache.c @@ -13,7 +13,7 @@ extern volatile intgo runtime·MemProfileRate; // dummy MSpan that contains no free objects. -static MSpan runtime·emptymspan; +MSpan runtime·emptymspan; MCache* runtime·allocmcache(void) diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c index 54728d5ada..c92fa1db73 100644 --- a/src/runtime/mgc0.c +++ b/src/runtime/mgc0.c @@ -120,7 +120,7 @@ FinBlock* runtime·finc; // cache of free blocks static byte finptrmask[FinBlockSize/PtrSize/PointersPerByte]; bool runtime·fingwait; bool runtime·fingwake; -static FinBlock *runtime·allfin; // list of all blocks +FinBlock *runtime·allfin; // list of all blocks BitVector runtime·gcdatamask; BitVector runtime·gcbssmask; @@ -140,7 +140,7 @@ static BitVector unrollglobgcprog(byte *prog, uintptr size); void runtime·bgsweep(void); static FuncVal bgsweepv = {runtime·bgsweep}; -static struct { +struct { uint64 full; // lock-free list of full blocks uint64 empty; // lock-free list of empty blocks byte pad0[CacheLineSize]; // prevents false-sharing between full/empty and nproc/nwait @@ -1038,7 +1038,6 @@ runtime·MSpan_Sweep(MSpan *s, bool preserve) // State of background runtime·sweep. // Protected by runtime·gclock. -// Must match mgc0.go. struct { G* g; diff --git a/src/runtime/os_windows.c b/src/runtime/os_windows.c index 62d94b65a0..6546d51d33 100644 --- a/src/runtime/os_windows.c +++ b/src/runtime/os_windows.c @@ -147,7 +147,7 @@ runtime·get_random_data(byte **rnd, int32 *rnd_len) void runtime·goenvs(void) { - extern Slice syscall·envs; + extern Slice runtime·envs; uint16 *env; String *s; @@ -160,8 +160,8 @@ runtime·goenvs(void) for(p=env; *p; n++) p += runtime·findnullw(p)+1; - syscall·envs = runtime·makeStringSlice(n); - s = (String*)syscall·envs.array; + runtime·envs = runtime·makeStringSlice(n); + s = (String*)runtime·envs.array; p = env; for(i=0; im->locks++; // disable GC because it can be called from sysmon if(g->m->p == nil) acquirep(p); // temporarily borrow p for mallocs in this function - if(mtype == nil) { - Eface e; - runtime·gc_m_ptr(&e); - mtype = ((PtrType*)e.type)->elem; - } - - mp = runtime·cnew(mtype); + mp = runtime·newM(); mcommoninit(mp); // In case of cgo or Solaris, pthread_create will make us a stack. @@ -889,19 +884,12 @@ runtime·allocm(P *p) return mp; } +G *runtime·newG(void); // in proc.go + static G* allocg(void) { - G *gp; - static Type *gtype; - - if(gtype == nil) { - Eface e; - runtime·gc_g_ptr(&e); - gtype = ((PtrType*)e.type)->elem; - } - gp = runtime·cnew(gtype); - return gp; + return runtime·newG(); } static M* lockextra(bool nilokay); diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 9b95868594..4bb661b54b 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -202,6 +202,14 @@ func newP() *p { return new(p) } +func newM() *m { + return new(m) +} + +func newG() *g { + return new(g) +} + func allgadd(gp *g) { if readgstatus(gp) == _Gidle { gothrow("allgadd: bad status Gidle") diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index aa8dd8f7a0..b3503fb909 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -62,10 +62,12 @@ runtime·mchr(byte *p, byte c, byte *ep) } static int32 argc; + +#pragma dataflag NOPTR /* argv not a heap pointer */ static uint8** argv; -Slice os·Args; -Slice syscall·envs; +extern Slice runtime·argslice; +extern Slice runtime·envs; void (*runtime·sysargs)(int32, uint8**); @@ -97,8 +99,8 @@ runtime·goargs(void) if(Windows) return; - os·Args = runtime·makeStringSlice(argc); - s = (String*)os·Args.array; + runtime·argslice = runtime·makeStringSlice(argc); + s = (String*)runtime·argslice.array; for(i=0; i Date: Wed, 24 Sep 2014 14:45:11 -0700 Subject: build: fix elf builds Corrections due to new strict type rules for data+bss. Also disable misc/cgo/cdefstest since you can't compile C code anymore. TBR=iant CC=golang-codereviews https://golang.org/cl/148050044 --- include/link.h | 2 +- src/cmd/ld/data.c | 13 ++++++++++--- src/cmd/ld/lib.c | 4 +++- src/run.bash | 11 +++++++---- src/runtime/vdso_linux_amd64.c | 9 +++++++-- 5 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src/cmd') diff --git a/include/link.h b/include/link.h index 292b077394..845f9338d9 100644 --- a/include/link.h +++ b/include/link.h @@ -204,10 +204,10 @@ enum SELFSECT, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, + SWINDOWS, SNOPTRDATA, SINITARR, SDATA, - SWINDOWS, SBSS, SNOPTRBSS, STLSBSS, diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 9d224d9eb9..89226bfe28 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -625,6 +625,7 @@ addstrdata(char *name, char *value) sp = linklookup(ctxt, p, 0); free(p); addstring(sp, value); + sp->type = SRODATA; s = linklookup(ctxt, name, 0); s->size = 0; @@ -816,9 +817,15 @@ proggenaddsym(ProgGen *g, LSym *s) proggenskip(g, g->pos, s->value - g->pos); g->pos += s->value - g->pos; - if(s->gotype == nil && s->size >= PtrSize) { + // The test for names beginning with . here is meant + // to keep .dynamic and .dynsym from turning up as + // conservative symbols. They should be marked SELFSECT + // and not SDATA, but sometimes that doesn't happen. + // Leave debugging the SDATA issue for the Go rewrite. + + if(s->gotype == nil && s->size >= PtrSize && s->name[0] != '.') { // conservative scan - diag("missing Go type information for global symbol: %s", s->name); + diag("missing Go type information for global symbol: %s size %d", s->name, (int)s->size); if((s->size%PtrSize) || (g->pos%PtrSize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); @@ -834,7 +841,7 @@ proggenaddsym(ProgGen *g, LSym *s) proggenarrayend(g); } g->pos = s->value + size; - } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize) { + } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize || s->name[0] == '.') { // no scan if(s->size < 32*PtrSize) { // Emit small symbols as data. diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 36f0f99de2..3edf7253d4 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -221,8 +221,10 @@ loadlib(void) // Provided by the code that imports the package. // Since we are simulating the import, we have to provide this string. cgostrsym = "go.string.\"runtime/cgo\""; - if(linkrlookup(ctxt, cgostrsym, 0) == nil) + if(linkrlookup(ctxt, cgostrsym, 0) == nil) { addstrdata(cgostrsym, "runtime/cgo"); + linklookup(ctxt, cgostrsym, 0)->type = SRODATA; + } } if(linkmode == LinkAuto) { diff --git a/src/run.bash b/src/run.bash index 4966cf1aa6..3c9430c87e 100755 --- a/src/run.bash +++ b/src/run.bash @@ -167,10 +167,13 @@ esac # This tests cgo -cdefs. That mode is not supported, # so it's okay if it doesn't work on some systems. # In particular, it works badly with clang on OS X. -[ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || -(xcd ../misc/cgo/testcdefs -./test.bash || exit 1 -) || exit $? +# It doesn't work at all now that we disallow C code +# outside runtime. Once runtime has no C code it won't +# even be necessary. +# [ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || +# (xcd ../misc/cgo/testcdefs +# ./test.bash || exit 1 +# ) || exit $? [ "$CGO_ENABLED" != 1 ] || [ "$GOOS" == darwin ] || (xcd ../misc/cgo/testgodefs diff --git a/src/runtime/vdso_linux_amd64.c b/src/runtime/vdso_linux_amd64.c index 38e1152438..41a41fdd6a 100644 --- a/src/runtime/vdso_linux_amd64.c +++ b/src/runtime/vdso_linux_amd64.c @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include "runtime.h" +#include "textflag.h" // Look up symbols in the Linux vDSO. @@ -171,14 +172,18 @@ struct vdso_info { Elf64_Verdef *verdef; }; +#pragma dataflag NOPTR static version_key linux26 = { (byte*)"LINUX_2.6", 0x3ae75f6 }; // initialize with vsyscall fallbacks +#pragma dataflag NOPTR void* runtime·__vdso_time_sym = (void*)0xffffffffff600400ULL; +#pragma dataflag NOPTR void* runtime·__vdso_gettimeofday_sym = (void*)0xffffffffff600000ULL; +#pragma dataflag NOPTR void* runtime·__vdso_clock_gettime_sym = (void*)0; -#define SYM_KEYS_COUNT 3 +#pragma dataflag NOPTR static symbol_key sym_keys[] = { { (byte*)"__vdso_time", 0xa33c485, &runtime·__vdso_time_sym }, { (byte*)"__vdso_gettimeofday", 0x315ca59, &runtime·__vdso_gettimeofday_sym }, @@ -301,7 +306,7 @@ vdso_parse_symbols(struct vdso_info *vdso_info, int32 version) if(vdso_info->valid == false) return; - for(i=0; ibucket[sym_keys[i].sym_hash % vdso_info->nbucket]; chain != 0; chain = vdso_info->chain[chain]) { -- cgit v1.3 From 39cd39b0238b079227460448f298bdd097562e7e Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Thu, 25 Sep 2014 01:49:04 +0400 Subject: cmd/go: strip -fsanitize= flags when building cgo object Fixes #8788. LGTM=iant R=iant CC=golang-codereviews https://golang.org/cl/142470043 --- src/cmd/go/build.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 2e52731529..27bd307378 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -2228,6 +2228,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, gccfi strings.HasSuffix(f, ".so"), strings.HasSuffix(f, ".dll"): continue + // Remove any -fsanitize=foo flags. + // Otherwise the compiler driver thinks that we are doing final link + // and links sanitizer runtime into the object file. But we are not doing + // the final link, we will link the resulting object file again. And + // so the program ends up with two copies of sanitizer runtime. + // See issue 8788 for details. + case strings.HasPrefix(f, "-fsanitize="): + continue default: bareLDFLAGS = append(bareLDFLAGS, f) } -- cgit v1.3 From 6077f0fc32a401f9a7b9540b6d1b00e855018c9a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 24 Sep 2014 19:18:01 -0400 Subject: cmd/go: fix bytes and net the right way Not sure why they used empty.s and all these other packages were special cased in cmd/go instead. Add them to the list. This avoids problems with net .s files being compiled with gcc in cgo mode and gcc not supporting // comments on ARM. Not a problem with bytes, but be consistent. The last change fixed the ARM build but broke the Windows build. Maybe *this* will make everyone happy. Sigh. TBR=iant CC=golang-codereviews https://golang.org/cl/144530046 --- src/bytes/bytes.s | 5 ----- src/cmd/go/build.go | 2 +- src/net/empty.s | 10 ---------- 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 src/bytes/bytes.s delete mode 100644 src/net/empty.s (limited to 'src/cmd') diff --git a/src/bytes/bytes.s b/src/bytes/bytes.s deleted file mode 100644 index 55103bae05..0000000000 --- a/src/bytes/bytes.s +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2013 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. - -// This file is here just to make the go tool happy. diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 27bd307378..fcc6b699be 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1630,7 +1630,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs [] extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) if p.Standard { switch p.ImportPath { - case "os", "runtime/pprof", "sync", "time": + case "bytes", "net", "os", "runtime/pprof", "sync", "time": extFiles++ } } diff --git a/src/net/empty.s b/src/net/empty.s deleted file mode 100644 index f0b255a0cf..0000000000 --- a/src/net/empty.s +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2013 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. - -// +build !cgo - -// This file is required to prevent compiler errors -// when the package built with CGO_ENABLED=0. -// Otherwise the compiler says: -// pkg/net/fd_poll_runtime.go:15: missing function body -- cgit v1.3 From 1b6807bb069c528447270c3d6c66c5c7597f388f Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 25 Sep 2014 07:59:01 -0700 Subject: cgo: adjust return value location to account for stack copies. During a cgo call, the stack can be copied. This copy invalidates the pointer that cgo has into the return value area. To fix this problem, pass the address of the location containing the stack top value (which is in the G struct). For cgo functions which return values, read the stktop before and after the cgo call to compute the adjustment necessary to write the return value. Fixes #8771 LGTM=iant, rsc R=iant, rsc, khr CC=golang-codereviews https://golang.org/cl/144130043 --- misc/cgo/test/callback.go | 45 +++++++++++++++++++++ misc/cgo/test/callback_c.c | 16 ++++++++ misc/cgo/test/cgo_test.go | 98 +++++++++++++++++++++++---------------------- src/cmd/cgo/out.go | 16 +++++++- src/runtime/asm_386.s | 10 +++++ src/runtime/asm_amd64.s | 11 +++++ src/runtime/asm_arm.s | 8 ++++ src/runtime/cgo/callbacks.c | 3 ++ src/runtime/stack.c | 4 +- 9 files changed, 161 insertions(+), 50 deletions(-) (limited to 'src/cmd') diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go index a7f1a3ecd6..44167e6e9e 100644 --- a/misc/cgo/test/callback.go +++ b/misc/cgo/test/callback.go @@ -10,6 +10,9 @@ void callGoFoo(void); void callGoStackCheck(void); void callPanic(void); void callCgoAllocate(void); +int callGoReturnVal(void); +int returnAfterGrow(void); +int returnAfterGrowFromGo(void); */ import "C" @@ -212,6 +215,48 @@ func testAllocateFromC(t *testing.T) { C.callCgoAllocate() // crashes or exits on failure } +// Test that C code can return a value if it calls a Go function that +// causes a stack copy. +func testReturnAfterGrow(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrow()) + }() + if got, want := <-c, 123456; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +// Test that we can return a value from Go->C->Go if the Go code +// causes a stack copy. +func testReturnAfterGrowFromGo(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrowFromGo()) + }() + if got, want := <-c, 129*128/2; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +//export goReturnVal +func goReturnVal() (r C.int) { + // Force a stack copy. + var f func(int) int + f = func(i int) int { + var buf [256]byte + use(buf[:]) + if i == 0 { + return 0 + } + return i + f(i-1) + } + r = C.int(f(128)) + return +} + func testCallbackStack(t *testing.T) { // Make cgo call and callback with different amount of stack stack available. // We do not do any explicit checks, just ensure that it does not crash. diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c index dcd4ddd4ee..5bb6425340 100644 --- a/misc/cgo/test/callback_c.c +++ b/misc/cgo/test/callback_c.c @@ -64,3 +64,19 @@ callGoStackCheck(void) extern void goStackCheck(void); goStackCheck(); } + +int +returnAfterGrow(void) +{ + extern int goReturnVal(void); + goReturnVal(); + return 123456; +} + +int +returnAfterGrowFromGo(void) +{ + extern int goReturnVal(void); + return goReturnVal(); +} + diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 1d1abf7291..fcfad83049 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -10,53 +10,55 @@ import "testing" // so that they can use cgo (import "C"). // These wrappers are here for gotest to find. -func TestAlign(t *testing.T) { testAlign(t) } -func TestConst(t *testing.T) { testConst(t) } -func TestEnum(t *testing.T) { testEnum(t) } -func TestAtol(t *testing.T) { testAtol(t) } -func TestErrno(t *testing.T) { testErrno(t) } -func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } -func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } -func TestCallback(t *testing.T) { testCallback(t) } -func TestCallbackGC(t *testing.T) { testCallbackGC(t) } -func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } -func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } -func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } -func TestPanicFromC(t *testing.T) { testPanicFromC(t) } -func TestAllocateFromC(t *testing.T) { testAllocateFromC(t) } -func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } -func TestBlocking(t *testing.T) { testBlocking(t) } -func Test1328(t *testing.T) { test1328(t) } -func TestParallelSleep(t *testing.T) { testParallelSleep(t) } -func TestSetEnv(t *testing.T) { testSetEnv(t) } -func TestHelpers(t *testing.T) { testHelpers(t) } -func TestLibgcc(t *testing.T) { testLibgcc(t) } -func Test1635(t *testing.T) { test1635(t) } -func TestPrintf(t *testing.T) { testPrintf(t) } -func Test4029(t *testing.T) { test4029(t) } -func TestBoolAlign(t *testing.T) { testBoolAlign(t) } -func Test3729(t *testing.T) { test3729(t) } -func Test3775(t *testing.T) { test3775(t) } -func TestCthread(t *testing.T) { testCthread(t) } -func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } -func Test5227(t *testing.T) { test5227(t) } -func TestCflags(t *testing.T) { testCflags(t) } -func Test5337(t *testing.T) { test5337(t) } -func Test5548(t *testing.T) { test5548(t) } -func Test5603(t *testing.T) { test5603(t) } -func Test6833(t *testing.T) { test6833(t) } -func Test3250(t *testing.T) { test3250(t) } -func TestCallbackStack(t *testing.T) { testCallbackStack(t) } -func TestFpVar(t *testing.T) { testFpVar(t) } -func Test4339(t *testing.T) { test4339(t) } -func Test6390(t *testing.T) { test6390(t) } -func Test5986(t *testing.T) { test5986(t) } -func Test7665(t *testing.T) { test7665(t) } -func TestNaming(t *testing.T) { testNaming(t) } -func Test7560(t *testing.T) { test7560(t) } -func Test5242(t *testing.T) { test5242(t) } -func Test8092(t *testing.T) { test8092(t) } -func Test7978(t *testing.T) { test7978(t) } -func Test8694(t *testing.T) { test8694(t) } +func TestAlign(t *testing.T) { testAlign(t) } +func TestConst(t *testing.T) { testConst(t) } +func TestEnum(t *testing.T) { testEnum(t) } +func TestAtol(t *testing.T) { testAtol(t) } +func TestErrno(t *testing.T) { testErrno(t) } +func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } +func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } +func TestCallback(t *testing.T) { testCallback(t) } +func TestCallbackGC(t *testing.T) { testCallbackGC(t) } +func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } +func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } +func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } +func TestPanicFromC(t *testing.T) { testPanicFromC(t) } +func TestAllocateFromC(t *testing.T) { testAllocateFromC(t) } +func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } +func TestBlocking(t *testing.T) { testBlocking(t) } +func Test1328(t *testing.T) { test1328(t) } +func TestParallelSleep(t *testing.T) { testParallelSleep(t) } +func TestSetEnv(t *testing.T) { testSetEnv(t) } +func TestHelpers(t *testing.T) { testHelpers(t) } +func TestLibgcc(t *testing.T) { testLibgcc(t) } +func Test1635(t *testing.T) { test1635(t) } +func TestPrintf(t *testing.T) { testPrintf(t) } +func Test4029(t *testing.T) { test4029(t) } +func TestBoolAlign(t *testing.T) { testBoolAlign(t) } +func Test3729(t *testing.T) { test3729(t) } +func Test3775(t *testing.T) { test3775(t) } +func TestCthread(t *testing.T) { testCthread(t) } +func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } +func Test5227(t *testing.T) { test5227(t) } +func TestCflags(t *testing.T) { testCflags(t) } +func Test5337(t *testing.T) { test5337(t) } +func Test5548(t *testing.T) { test5548(t) } +func Test5603(t *testing.T) { test5603(t) } +func Test6833(t *testing.T) { test6833(t) } +func Test3250(t *testing.T) { test3250(t) } +func TestCallbackStack(t *testing.T) { testCallbackStack(t) } +func TestFpVar(t *testing.T) { testFpVar(t) } +func Test4339(t *testing.T) { test4339(t) } +func Test6390(t *testing.T) { test6390(t) } +func Test5986(t *testing.T) { test5986(t) } +func Test7665(t *testing.T) { test7665(t) } +func TestNaming(t *testing.T) { testNaming(t) } +func Test7560(t *testing.T) { test7560(t) } +func Test5242(t *testing.T) { test5242(t) } +func Test8092(t *testing.T) { test8092(t) } +func Test7978(t *testing.T) { test7978(t) } +func Test8694(t *testing.T) { test8694(t) } +func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } +func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 2d14f766fc..4e5b3a2454 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -44,6 +44,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") + fmt.Fprintf(fm, "char* cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides crosscall2. We just need a prototype. @@ -519,9 +520,13 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Use packed attribute to force no padding in this struct in case // gcc has different packing requirements. fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) + if n.FuncType.Result != nil { + // Save the stack top for use below. + fmt.Fprintf(fgcc, "\tchar *stktop = cgo_topofstack();\n") + } fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { - fmt.Fprintf(fgcc, "a->r = ") + fmt.Fprintf(fgcc, "__typeof__(a->r) r = ") if c := t.C.String(); c[len(c)-1] == '*' { fmt.Fprint(fgcc, "(__typeof__(a->r)) ") } @@ -544,6 +549,13 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, "a->p%d", i) } fmt.Fprintf(fgcc, ");\n") + if n.FuncType.Result != nil { + // The cgo call may have caused a stack copy (via a callback). + // Adjust the return value pointer appropriately. + fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (cgo_topofstack() - stktop));\n") + // Save the return value. + fmt.Fprintf(fgcc, "\ta->r = r;\n") + } if n.AddError { fmt.Fprintf(fgcc, "\treturn errno;\n") } @@ -1131,6 +1143,8 @@ __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) +extern char* cgo_topofstack(void); + #include #include ` diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 846a214d55..f1b3346e83 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -2275,3 +2275,13 @@ TEXT runtime·fastrand1(SB), NOSPLIT, $0-4 TEXT runtime·return0(SB), NOSPLIT, $0 MOVL $0, AX RET + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + get_tls(CX) + MOVL g(CX), AX + MOVL g_m(AX), AX + MOVL m_curg(AX), AX + MOVL (g_stack+stack_hi)(AX), AX + RET diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 7304d79a2f..b4c6c6bdca 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -2220,3 +2220,14 @@ TEXT runtime·fastrand1(SB), NOSPLIT, $0-4 TEXT runtime·return0(SB), NOSPLIT, $0 MOVL $0, AX RET + + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + get_tls(CX) + MOVQ g(CX), AX + MOVQ g_m(AX), AX + MOVQ m_curg(AX), AX + MOVQ (g_stack+stack_hi)(AX), AX + RET diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 38d97b78f3..2c5de8afb1 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -1300,3 +1300,11 @@ yieldloop: RET SUB $1, R1 B yieldloop + +// Called from cgo wrappers, this function returns g->m->curg.stack.hi. +// Must obey the gcc calling convention. +TEXT cgo_topofstack(SB),NOSPLIT,$0 + MOVW g_m(g), R0 + MOVW m_curg(R0), R0 + MOVW (g_stack+stack_hi)(R0), R0 + RET diff --git a/src/runtime/cgo/callbacks.c b/src/runtime/cgo/callbacks.c index 16614d03db..cea9b1667f 100644 --- a/src/runtime/cgo/callbacks.c +++ b/src/runtime/cgo/callbacks.c @@ -78,3 +78,6 @@ void (*_cgo_free)(void*) = x_cgo_free; #pragma cgo_import_static x_cgo_thread_start extern void x_cgo_thread_start(void*); void (*_cgo_thread_start)(void*) = x_cgo_thread_start; + +#pragma cgo_export_static cgo_topofstack +#pragma cgo_export_dynamic cgo_topofstack diff --git a/src/runtime/stack.c b/src/runtime/stack.c index 0d8814731c..2d23c717bd 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -827,7 +827,9 @@ runtime·shrinkstack(G *gp) if(used >= oldsize / 4) return; // still using at least 1/4 of the segment. - if(gp->syscallsp != 0) // TODO: can we handle this case? + // We can't copy the stack if we're in a syscall. + // The syscall might have pointers into the stack. + if(gp->syscallsp != 0) return; #ifdef GOOS_windows -- cgit v1.3 From 1aa65fe8d4c0ebdd754480d281f378fcd1c42cea Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 25 Sep 2014 08:37:04 -0700 Subject: runtime: add load_g call in arm callback. Need to restore the g register. Somehow this line vaporized from CL 144130043. Also cgo_topofstack -> _cgo_topofstack, that vaporized also. TBR=rsc CC=golang-codereviews https://golang.org/cl/150940044 --- src/cmd/cgo/out.go | 8 ++++---- src/runtime/asm_386.s | 2 +- src/runtime/asm_amd64.s | 2 +- src/runtime/asm_arm.s | 3 ++- src/runtime/cgo/callbacks.c | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 4e5b3a2454..d92bed9bf0 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -44,7 +44,7 @@ func (p *Package) writeDefs() { fmt.Fprintf(fm, "int main() { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") - fmt.Fprintf(fm, "char* cgo_topofstack(void) { return (char*)0; }\n") + fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") } else { // If we're not importing runtime/cgo, we *are* runtime/cgo, // which provides crosscall2. We just need a prototype. @@ -522,7 +522,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute()) if n.FuncType.Result != nil { // Save the stack top for use below. - fmt.Fprintf(fgcc, "\tchar *stktop = cgo_topofstack();\n") + fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n") } fmt.Fprintf(fgcc, "\t") if t := n.FuncType.Result; t != nil { @@ -552,7 +552,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { if n.FuncType.Result != nil { // The cgo call may have caused a stack copy (via a callback). // Adjust the return value pointer appropriately. - fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (cgo_topofstack() - stktop));\n") + fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n") // Save the return value. fmt.Fprintf(fgcc, "\ta->r = r;\n") } @@ -1143,7 +1143,7 @@ __cgo_size_assert(__cgo_long_long, 8) __cgo_size_assert(float, 4) __cgo_size_assert(double, 8) -extern char* cgo_topofstack(void); +extern char* _cgo_topofstack(void); #include #include diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index f1b3346e83..1495246a25 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -2278,7 +2278,7 @@ TEXT runtime·return0(SB), NOSPLIT, $0 // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 get_tls(CX) MOVL g(CX), AX MOVL g_m(AX), AX diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index b4c6c6bdca..3f7f608410 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -2224,7 +2224,7 @@ TEXT runtime·return0(SB), NOSPLIT, $0 // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), AX diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index 2c5de8afb1..06bd0751db 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -1303,7 +1303,8 @@ yieldloop: // Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Must obey the gcc calling convention. -TEXT cgo_topofstack(SB),NOSPLIT,$0 +TEXT _cgo_topofstack(SB),NOSPLIT,$0 + BL runtime·load_g(SB) MOVW g_m(g), R0 MOVW m_curg(R0), R0 MOVW (g_stack+stack_hi)(R0), R0 diff --git a/src/runtime/cgo/callbacks.c b/src/runtime/cgo/callbacks.c index cea9b1667f..282beeea88 100644 --- a/src/runtime/cgo/callbacks.c +++ b/src/runtime/cgo/callbacks.c @@ -79,5 +79,5 @@ void (*_cgo_free)(void*) = x_cgo_free; extern void x_cgo_thread_start(void*); void (*_cgo_thread_start)(void*) = x_cgo_thread_start; -#pragma cgo_export_static cgo_topofstack -#pragma cgo_export_dynamic cgo_topofstack +#pragma cgo_export_static _cgo_topofstack +#pragma cgo_export_dynamic _cgo_topofstack -- cgit v1.3 From 53c66543e022b1a96a599fee0819f6b16e92bead Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:08:37 -0400 Subject: cmd/gc: avoid infinite recursion on invalid recursive type Fixes #8507. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/144560043 --- src/cmd/gc/align.c | 4 +++- src/cmd/gc/subr.c | 3 ++- test/fixedbugs/issue8507.go | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/fixedbugs/issue8507.go (limited to 'src/cmd') diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index b809640e42..6e5d149c75 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -119,8 +119,10 @@ dowidth(Type *t) if(t->width == -2) { lno = lineno; lineno = t->lineno; - if(!t->broke) + if(!t->broke) { + t->broke = 1; yyerror("invalid recursive type %T", t); + } t->width = 0; lineno = lno; return; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 666be96679..c3bc5af3b8 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -529,7 +529,8 @@ algtype1(Type *t, Type **bad) if(bad) *bad = T; - + if(t->broke) + return AMEM; if(t->noalg) return ANOEQ; diff --git a/test/fixedbugs/issue8507.go b/test/fixedbugs/issue8507.go new file mode 100644 index 0000000000..00a14aa88f --- /dev/null +++ b/test/fixedbugs/issue8507.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2014 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. + +// issue 8507 +// used to call algtype on invalid recursive type and get into infinite recursion + +package p + +type T struct{ T } // ERROR "invalid recursive type T" + +func f() { + println(T{} == T{}) +} -- cgit v1.3 From 52e9bcafe1b5bdae3354ddf82879751dfe6eb25b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:13:02 -0400 Subject: cmd/gc: print x++ (not x += 1) in errors about x++ Fixes #8311. LGTM=iant R=golang-codereviews, iant CC=golang-codereviews, r https://golang.org/cl/146270043 --- src/cmd/gc/fmt.c | 7 + src/cmd/gc/go.y | 2 + src/cmd/gc/typecheck.c | 4 + src/cmd/gc/y.tab.c | 526 ++++++++++++++++++++++---------------------- test/fixedbugs/issue8311.go | 16 ++ 5 files changed, 293 insertions(+), 262 deletions(-) create mode 100644 test/fixedbugs/issue8311.go (limited to 'src/cmd') diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index f67757449b..89d2a14046 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -810,6 +810,13 @@ stmtfmt(Fmt *f, Node *n) break; case OASOP: + if(n->implicit) { + if(n->etype == OADD) + fmtprint(f, "%N++", n->left); + else + fmtprint(f, "%N--", n->left); + break; + } fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right); break; diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index b16f64b5d6..68fccc1ad1 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -460,11 +460,13 @@ simple_stmt: | expr LINC { $$ = nod(OASOP, $1, nodintconst(1)); + $$->implicit = 1; $$->etype = OADD; } | expr LDEC { $$ = nod(OASOP, $1, nodintconst(1)); + $$->implicit = 1; $$->etype = OSUB; } diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 298920bfec..9440526060 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -600,6 +600,10 @@ reswitch: } if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { defaultlit2(&l, &r, 1); + if(n->op == OASOP && n->implicit) { + yyerror("invalid operation: %N (non-numeric type %T)", n, l->type); + goto error; + } yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type); goto error; } diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 47beae03b6..f464126ac9 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -663,37 +663,37 @@ static const yytype_uint16 yyrline[] = 263, 264, 271, 271, 284, 288, 289, 293, 298, 304, 308, 312, 316, 322, 328, 334, 339, 343, 347, 353, 359, 363, 367, 373, 377, 383, 384, 388, 394, 403, - 409, 427, 432, 444, 460, 465, 472, 492, 510, 519, - 538, 537, 552, 551, 583, 586, 593, 592, 603, 609, - 616, 623, 634, 640, 643, 651, 650, 661, 667, 679, - 683, 688, 678, 709, 708, 721, 724, 730, 733, 745, - 749, 744, 767, 766, 782, 783, 787, 791, 795, 799, - 803, 807, 811, 815, 819, 823, 827, 831, 835, 839, - 843, 847, 851, 855, 860, 866, 867, 871, 882, 886, - 890, 894, 899, 903, 913, 917, 922, 930, 934, 935, - 946, 950, 954, 958, 962, 970, 971, 977, 984, 990, - 997, 1000, 1007, 1013, 1030, 1037, 1038, 1045, 1046, 1065, - 1066, 1069, 1072, 1076, 1087, 1096, 1102, 1105, 1108, 1115, - 1116, 1122, 1135, 1150, 1158, 1170, 1175, 1181, 1182, 1183, - 1184, 1185, 1186, 1192, 1193, 1194, 1195, 1201, 1202, 1203, - 1204, 1205, 1211, 1212, 1215, 1218, 1219, 1220, 1221, 1222, - 1225, 1226, 1239, 1243, 1248, 1253, 1258, 1262, 1263, 1266, - 1272, 1279, 1285, 1292, 1298, 1309, 1324, 1353, 1391, 1416, - 1434, 1443, 1446, 1454, 1458, 1462, 1469, 1475, 1480, 1492, - 1495, 1506, 1507, 1513, 1514, 1520, 1524, 1530, 1531, 1537, - 1541, 1547, 1570, 1575, 1581, 1587, 1594, 1603, 1612, 1627, - 1633, 1638, 1642, 1649, 1662, 1663, 1669, 1675, 1678, 1682, - 1688, 1691, 1700, 1703, 1704, 1708, 1709, 1715, 1716, 1717, - 1718, 1719, 1721, 1720, 1735, 1741, 1745, 1749, 1753, 1757, - 1762, 1781, 1787, 1795, 1799, 1805, 1809, 1815, 1819, 1825, - 1829, 1838, 1842, 1846, 1850, 1856, 1859, 1867, 1868, 1870, - 1871, 1874, 1877, 1880, 1883, 1886, 1889, 1892, 1895, 1898, - 1901, 1904, 1907, 1910, 1913, 1919, 1923, 1927, 1931, 1935, - 1939, 1959, 1966, 1977, 1978, 1979, 1982, 1983, 1986, 1990, - 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2030, 2036, 2044, - 2052, 2058, 2065, 2081, 2103, 2107, 2113, 2116, 2119, 2123, - 2133, 2137, 2156, 2164, 2165, 2177, 2178, 2181, 2185, 2191, - 2195, 2201, 2205 + 409, 427, 432, 444, 460, 466, 474, 494, 512, 521, + 540, 539, 554, 553, 585, 588, 595, 594, 605, 611, + 618, 625, 636, 642, 645, 653, 652, 663, 669, 681, + 685, 690, 680, 711, 710, 723, 726, 732, 735, 747, + 751, 746, 769, 768, 784, 785, 789, 793, 797, 801, + 805, 809, 813, 817, 821, 825, 829, 833, 837, 841, + 845, 849, 853, 857, 862, 868, 869, 873, 884, 888, + 892, 896, 901, 905, 915, 919, 924, 932, 936, 937, + 948, 952, 956, 960, 964, 972, 973, 979, 986, 992, + 999, 1002, 1009, 1015, 1032, 1039, 1040, 1047, 1048, 1067, + 1068, 1071, 1074, 1078, 1089, 1098, 1104, 1107, 1110, 1117, + 1118, 1124, 1137, 1152, 1160, 1172, 1177, 1183, 1184, 1185, + 1186, 1187, 1188, 1194, 1195, 1196, 1197, 1203, 1204, 1205, + 1206, 1207, 1213, 1214, 1217, 1220, 1221, 1222, 1223, 1224, + 1227, 1228, 1241, 1245, 1250, 1255, 1260, 1264, 1265, 1268, + 1274, 1281, 1287, 1294, 1300, 1311, 1326, 1355, 1393, 1418, + 1436, 1445, 1448, 1456, 1460, 1464, 1471, 1477, 1482, 1494, + 1497, 1508, 1509, 1515, 1516, 1522, 1526, 1532, 1533, 1539, + 1543, 1549, 1572, 1577, 1583, 1589, 1596, 1605, 1614, 1629, + 1635, 1640, 1644, 1651, 1664, 1665, 1671, 1677, 1680, 1684, + 1690, 1693, 1702, 1705, 1706, 1710, 1711, 1717, 1718, 1719, + 1720, 1721, 1723, 1722, 1737, 1743, 1747, 1751, 1755, 1759, + 1764, 1783, 1789, 1797, 1801, 1807, 1811, 1817, 1821, 1827, + 1831, 1840, 1844, 1848, 1852, 1858, 1861, 1869, 1870, 1872, + 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894, 1897, 1900, + 1903, 1906, 1909, 1912, 1915, 1921, 1925, 1929, 1933, 1937, + 1941, 1961, 1968, 1979, 1980, 1981, 1984, 1985, 1988, 1992, + 2002, 2006, 2010, 2014, 2018, 2022, 2026, 2032, 2038, 2046, + 2054, 2060, 2067, 2083, 2105, 2109, 2115, 2118, 2121, 2125, + 2135, 2139, 2158, 2166, 2167, 2179, 2180, 2183, 2187, 2193, + 2197, 2203, 2207 }; #endif @@ -2781,20 +2781,22 @@ yyreduce: #line 461 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); + (yyval.node)->implicit = 1; (yyval.node)->etype = OADD; } break; case 55: -#line 466 "go.y" +#line 467 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); + (yyval.node)->implicit = 1; (yyval.node)->etype = OSUB; } break; case 56: -#line 473 "go.y" +#line 475 "go.y" { Node *n, *nn; @@ -2817,7 +2819,7 @@ yyreduce: break; case 57: -#line 493 "go.y" +#line 495 "go.y" { Node *n; @@ -2838,7 +2840,7 @@ yyreduce: break; case 58: -#line 511 "go.y" +#line 513 "go.y" { // will be converted to OCASE // right will point to next case @@ -2850,7 +2852,7 @@ yyreduce: break; case 59: -#line 520 "go.y" +#line 522 "go.y" { Node *n, *nn; @@ -2869,14 +2871,14 @@ yyreduce: break; case 60: -#line 538 "go.y" +#line 540 "go.y" { markdcl(); } break; case 61: -#line 542 "go.y" +#line 544 "go.y" { if((yyvsp[(3) - (4)].list) == nil) (yyval.node) = nod(OEMPTY, N, N); @@ -2887,7 +2889,7 @@ yyreduce: break; case 62: -#line 552 "go.y" +#line 554 "go.y" { // If the last token read by the lexer was consumed // as part of the case, clear it (parser has cleared yychar). @@ -2901,7 +2903,7 @@ yyreduce: break; case 63: -#line 563 "go.y" +#line 565 "go.y" { int last; @@ -2923,28 +2925,28 @@ yyreduce: break; case 64: -#line 583 "go.y" +#line 585 "go.y" { (yyval.list) = nil; } break; case 65: -#line 587 "go.y" +#line 589 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: -#line 593 "go.y" +#line 595 "go.y" { markdcl(); } break; case 67: -#line 597 "go.y" +#line 599 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -2952,7 +2954,7 @@ yyreduce: break; case 68: -#line 604 "go.y" +#line 606 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2961,7 +2963,7 @@ yyreduce: break; case 69: -#line 610 "go.y" +#line 612 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -2971,7 +2973,7 @@ yyreduce: break; case 70: -#line 617 "go.y" +#line 619 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(2) - (2)].node)); (yyval.node)->etype = 0; // := flag @@ -2979,7 +2981,7 @@ yyreduce: break; case 71: -#line 624 "go.y" +#line 626 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -2993,7 +2995,7 @@ yyreduce: break; case 72: -#line 635 "go.y" +#line 637 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -3002,7 +3004,7 @@ yyreduce: break; case 74: -#line 644 "go.y" +#line 646 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -3010,14 +3012,14 @@ yyreduce: break; case 75: -#line 651 "go.y" +#line 653 "go.y" { markdcl(); } break; case 76: -#line 655 "go.y" +#line 657 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3025,7 +3027,7 @@ yyreduce: break; case 77: -#line 662 "go.y" +#line 664 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3034,7 +3036,7 @@ yyreduce: break; case 78: -#line 668 "go.y" +#line 670 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3045,14 +3047,14 @@ yyreduce: break; case 79: -#line 679 "go.y" +#line 681 "go.y" { markdcl(); } break; case 80: -#line 683 "go.y" +#line 685 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3060,14 +3062,14 @@ yyreduce: break; case 81: -#line 688 "go.y" +#line 690 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 82: -#line 692 "go.y" +#line 694 "go.y" { Node *n; NodeList *nn; @@ -3085,14 +3087,14 @@ yyreduce: break; case 83: -#line 709 "go.y" +#line 711 "go.y" { markdcl(); } break; case 84: -#line 713 "go.y" +#line 715 "go.y" { if((yyvsp[(4) - (5)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3102,28 +3104,28 @@ yyreduce: break; case 85: -#line 721 "go.y" +#line 723 "go.y" { (yyval.list) = nil; } break; case 86: -#line 725 "go.y" +#line 727 "go.y" { (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 87: -#line 730 "go.y" +#line 732 "go.y" { (yyval.list) = nil; } break; case 88: -#line 734 "go.y" +#line 736 "go.y" { NodeList *node; @@ -3135,14 +3137,14 @@ yyreduce: break; case 89: -#line 745 "go.y" +#line 747 "go.y" { markdcl(); } break; case 90: -#line 749 "go.y" +#line 751 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3153,7 +3155,7 @@ yyreduce: break; case 91: -#line 757 "go.y" +#line 759 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3164,14 +3166,14 @@ yyreduce: break; case 92: -#line 767 "go.y" +#line 769 "go.y" { typesw = nod(OXXX, typesw, N); } break; case 93: -#line 771 "go.y" +#line 773 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3181,154 +3183,154 @@ yyreduce: break; case 95: -#line 784 "go.y" +#line 786 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 96: -#line 788 "go.y" +#line 790 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 97: -#line 792 "go.y" +#line 794 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 98: -#line 796 "go.y" +#line 798 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 99: -#line 800 "go.y" +#line 802 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 100: -#line 804 "go.y" +#line 806 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 101: -#line 808 "go.y" +#line 810 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 102: -#line 812 "go.y" +#line 814 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 103: -#line 816 "go.y" +#line 818 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 104: -#line 820 "go.y" +#line 822 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 105: -#line 824 "go.y" +#line 826 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 106: -#line 828 "go.y" +#line 830 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 107: -#line 832 "go.y" +#line 834 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 108: -#line 836 "go.y" +#line 838 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: -#line 840 "go.y" +#line 842 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: -#line 844 "go.y" +#line 846 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: -#line 848 "go.y" +#line 850 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: -#line 852 "go.y" +#line 854 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: -#line 856 "go.y" +#line 858 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 114: -#line 861 "go.y" +#line 863 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 116: -#line 868 "go.y" +#line 870 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 117: -#line 872 "go.y" +#line 874 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3342,28 +3344,28 @@ yyreduce: break; case 118: -#line 883 "go.y" +#line 885 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; case 119: -#line 887 "go.y" +#line 889 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; case 120: -#line 891 "go.y" +#line 893 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; case 121: -#line 895 "go.y" +#line 897 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); @@ -3371,28 +3373,28 @@ yyreduce: break; case 122: -#line 900 "go.y" +#line 902 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; case 123: -#line 904 "go.y" +#line 906 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; case 124: -#line 914 "go.y" +#line 916 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; case 125: -#line 918 "go.y" +#line 920 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3400,7 +3402,7 @@ yyreduce: break; case 126: -#line 923 "go.y" +#line 925 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3409,14 +3411,14 @@ yyreduce: break; case 127: -#line 931 "go.y" +#line 933 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 129: -#line 936 "go.y" +#line 938 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3430,35 +3432,35 @@ yyreduce: break; case 130: -#line 947 "go.y" +#line 949 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; case 131: -#line 951 "go.y" +#line 953 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; case 132: -#line 955 "go.y" +#line 957 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; case 133: -#line 959 "go.y" +#line 961 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; case 134: -#line 963 "go.y" +#line 965 "go.y" { if((yyvsp[(5) - (8)].node) == N) yyerror("middle index required in 3-index slice"); @@ -3469,7 +3471,7 @@ yyreduce: break; case 136: -#line 972 "go.y" +#line 974 "go.y" { // conversion (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); @@ -3478,7 +3480,7 @@ yyreduce: break; case 137: -#line 978 "go.y" +#line 980 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3488,7 +3490,7 @@ yyreduce: break; case 138: -#line 985 "go.y" +#line 987 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3497,7 +3499,7 @@ yyreduce: break; case 139: -#line 991 "go.y" +#line 993 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3507,7 +3509,7 @@ yyreduce: break; case 141: -#line 1000 "go.y" +#line 1002 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3516,14 +3518,14 @@ yyreduce: break; case 142: -#line 1008 "go.y" +#line 1010 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 143: -#line 1014 "go.y" +#line 1016 "go.y" { // These nodes do not carry line numbers. // Since a composite literal commonly spans several lines, @@ -3543,7 +3545,7 @@ yyreduce: break; case 144: -#line 1031 "go.y" +#line 1033 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3551,7 +3553,7 @@ yyreduce: break; case 146: -#line 1039 "go.y" +#line 1041 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3559,7 +3561,7 @@ yyreduce: break; case 148: -#line 1047 "go.y" +#line 1049 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3579,21 +3581,21 @@ yyreduce: break; case 152: -#line 1073 "go.y" +#line 1075 "go.y" { (yyval.i) = LBODY; } break; case 153: -#line 1077 "go.y" +#line 1079 "go.y" { (yyval.i) = '{'; } break; case 154: -#line 1088 "go.y" +#line 1090 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3603,21 +3605,21 @@ yyreduce: break; case 155: -#line 1097 "go.y" +#line 1099 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; case 156: -#line 1102 "go.y" +#line 1104 "go.y" { (yyval.node) = N; } break; case 158: -#line 1109 "go.y" +#line 1111 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3627,14 +3629,14 @@ yyreduce: break; case 160: -#line 1117 "go.y" +#line 1119 "go.y" { (yyval.sym) = S; } break; case 161: -#line 1123 "go.y" +#line 1125 "go.y" { Pkg *p; @@ -3650,7 +3652,7 @@ yyreduce: break; case 162: -#line 1136 "go.y" +#line 1138 "go.y" { Pkg *p; @@ -3666,7 +3668,7 @@ yyreduce: break; case 163: -#line 1151 "go.y" +#line 1153 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3675,7 +3677,7 @@ yyreduce: break; case 165: -#line 1171 "go.y" +#line 1173 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); @@ -3683,35 +3685,35 @@ yyreduce: break; case 166: -#line 1176 "go.y" +#line 1178 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; case 172: -#line 1187 "go.y" +#line 1189 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 176: -#line 1196 "go.y" +#line 1198 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 181: -#line 1206 "go.y" +#line 1208 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); } break; case 191: -#line 1227 "go.y" +#line 1229 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3725,14 +3727,14 @@ yyreduce: break; case 192: -#line 1240 "go.y" +#line 1242 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; case 193: -#line 1244 "go.y" +#line 1246 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); @@ -3740,7 +3742,7 @@ yyreduce: break; case 194: -#line 1249 "go.y" +#line 1251 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; @@ -3748,7 +3750,7 @@ yyreduce: break; case 195: -#line 1254 "go.y" +#line 1256 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; @@ -3756,21 +3758,21 @@ yyreduce: break; case 196: -#line 1259 "go.y" +#line 1261 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; case 199: -#line 1267 "go.y" +#line 1269 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 200: -#line 1273 "go.y" +#line 1275 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; @@ -3778,7 +3780,7 @@ yyreduce: break; case 201: -#line 1280 "go.y" +#line 1282 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3787,7 +3789,7 @@ yyreduce: break; case 202: -#line 1286 "go.y" +#line 1288 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -3795,7 +3797,7 @@ yyreduce: break; case 203: -#line 1293 "go.y" +#line 1295 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3804,7 +3806,7 @@ yyreduce: break; case 204: -#line 1299 "go.y" +#line 1301 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -3812,7 +3814,7 @@ yyreduce: break; case 205: -#line 1310 "go.y" +#line 1312 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) @@ -3828,7 +3830,7 @@ yyreduce: break; case 206: -#line 1325 "go.y" +#line 1327 "go.y" { Node *t; @@ -3860,7 +3862,7 @@ yyreduce: break; case 207: -#line 1354 "go.y" +#line 1356 "go.y" { Node *rcvr, *t; @@ -3899,7 +3901,7 @@ yyreduce: break; case 208: -#line 1392 "go.y" +#line 1394 "go.y" { Sym *s; Type *t; @@ -3927,7 +3929,7 @@ yyreduce: break; case 209: -#line 1417 "go.y" +#line 1419 "go.y" { (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list)); @@ -3946,7 +3948,7 @@ yyreduce: break; case 210: -#line 1435 "go.y" +#line 1437 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -3956,14 +3958,14 @@ yyreduce: break; case 211: -#line 1443 "go.y" +#line 1445 "go.y" { (yyval.list) = nil; } break; case 212: -#line 1447 "go.y" +#line 1449 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -3972,21 +3974,21 @@ yyreduce: break; case 213: -#line 1455 "go.y" +#line 1457 "go.y" { (yyval.list) = nil; } break; case 214: -#line 1459 "go.y" +#line 1461 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; case 215: -#line 1463 "go.y" +#line 1465 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); @@ -3994,14 +3996,14 @@ yyreduce: break; case 216: -#line 1470 "go.y" +#line 1472 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; case 217: -#line 1476 "go.y" +#line 1478 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); @@ -4009,21 +4011,21 @@ yyreduce: break; case 218: -#line 1481 "go.y" +#line 1483 "go.y" { (yyval.node) = closurebody(nil); } break; case 219: -#line 1492 "go.y" +#line 1494 "go.y" { (yyval.list) = nil; } break; case 220: -#line 1496 "go.y" +#line 1498 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) @@ -4035,56 +4037,56 @@ yyreduce: break; case 222: -#line 1508 "go.y" +#line 1510 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 224: -#line 1515 "go.y" +#line 1517 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 225: -#line 1521 "go.y" +#line 1523 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 226: -#line 1525 "go.y" +#line 1527 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 228: -#line 1532 "go.y" +#line 1534 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 229: -#line 1538 "go.y" +#line 1540 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 230: -#line 1542 "go.y" +#line 1544 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 231: -#line 1548 "go.y" +#line 1550 "go.y" { NodeList *l; @@ -4110,7 +4112,7 @@ yyreduce: break; case 232: -#line 1571 "go.y" +#line 1573 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); @@ -4118,7 +4120,7 @@ yyreduce: break; case 233: -#line 1576 "go.y" +#line 1578 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4127,7 +4129,7 @@ yyreduce: break; case 234: -#line 1582 "go.y" +#line 1584 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4136,7 +4138,7 @@ yyreduce: break; case 235: -#line 1588 "go.y" +#line 1590 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4146,7 +4148,7 @@ yyreduce: break; case 236: -#line 1595 "go.y" +#line 1597 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4156,7 +4158,7 @@ yyreduce: break; case 237: -#line 1604 "go.y" +#line 1606 "go.y" { Node *n; @@ -4168,7 +4170,7 @@ yyreduce: break; case 238: -#line 1613 "go.y" +#line 1615 "go.y" { Pkg *pkg; @@ -4184,14 +4186,14 @@ yyreduce: break; case 239: -#line 1628 "go.y" +#line 1630 "go.y" { (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg); } break; case 240: -#line 1634 "go.y" +#line 1636 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); @@ -4199,14 +4201,14 @@ yyreduce: break; case 241: -#line 1639 "go.y" +#line 1641 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; case 242: -#line 1643 "go.y" +#line 1645 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); @@ -4214,7 +4216,7 @@ yyreduce: break; case 243: -#line 1650 "go.y" +#line 1652 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4225,7 +4227,7 @@ yyreduce: break; case 245: -#line 1664 "go.y" +#line 1666 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4234,7 +4236,7 @@ yyreduce: break; case 246: -#line 1670 "go.y" +#line 1672 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4243,56 +4245,56 @@ yyreduce: break; case 248: -#line 1679 "go.y" +#line 1681 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 249: -#line 1683 "go.y" +#line 1685 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 250: -#line 1688 "go.y" +#line 1690 "go.y" { (yyval.list) = nil; } break; case 251: -#line 1692 "go.y" +#line 1694 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 252: -#line 1700 "go.y" +#line 1702 "go.y" { (yyval.node) = N; } break; case 254: -#line 1705 "go.y" +#line 1707 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; case 256: -#line 1710 "go.y" +#line 1712 "go.y" { (yyval.node) = N; } break; case 262: -#line 1721 "go.y" +#line 1723 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions @@ -4300,7 +4302,7 @@ yyreduce: break; case 263: -#line 1726 "go.y" +#line 1728 "go.y" { NodeList *l; @@ -4313,7 +4315,7 @@ yyreduce: break; case 264: -#line 1736 "go.y" +#line 1738 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); @@ -4322,35 +4324,35 @@ yyreduce: break; case 265: -#line 1742 "go.y" +#line 1744 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; case 266: -#line 1746 "go.y" +#line 1748 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; case 267: -#line 1750 "go.y" +#line 1752 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; case 268: -#line 1754 "go.y" +#line 1756 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; case 269: -#line 1758 "go.y" +#line 1760 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions @@ -4358,7 +4360,7 @@ yyreduce: break; case 270: -#line 1763 "go.y" +#line 1765 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4378,7 +4380,7 @@ yyreduce: break; case 271: -#line 1782 "go.y" +#line 1784 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4387,7 +4389,7 @@ yyreduce: break; case 272: -#line 1788 "go.y" +#line 1790 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4396,189 +4398,189 @@ yyreduce: break; case 273: -#line 1796 "go.y" +#line 1798 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 274: -#line 1800 "go.y" +#line 1802 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 275: -#line 1806 "go.y" +#line 1808 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 276: -#line 1810 "go.y" +#line 1812 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 277: -#line 1816 "go.y" +#line 1818 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 278: -#line 1820 "go.y" +#line 1822 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 279: -#line 1826 "go.y" +#line 1828 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 280: -#line 1830 "go.y" +#line 1832 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 281: -#line 1839 "go.y" +#line 1841 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 282: -#line 1843 "go.y" +#line 1845 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 283: -#line 1847 "go.y" +#line 1849 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 284: -#line 1851 "go.y" +#line 1853 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 285: -#line 1856 "go.y" +#line 1858 "go.y" { (yyval.list) = nil; } break; case 286: -#line 1860 "go.y" +#line 1862 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 291: -#line 1874 "go.y" +#line 1876 "go.y" { (yyval.node) = N; } break; case 293: -#line 1880 "go.y" +#line 1882 "go.y" { (yyval.list) = nil; } break; case 295: -#line 1886 "go.y" +#line 1888 "go.y" { (yyval.node) = N; } break; case 297: -#line 1892 "go.y" +#line 1894 "go.y" { (yyval.list) = nil; } break; case 299: -#line 1898 "go.y" +#line 1900 "go.y" { (yyval.list) = nil; } break; case 301: -#line 1904 "go.y" +#line 1906 "go.y" { (yyval.list) = nil; } break; case 303: -#line 1910 "go.y" +#line 1912 "go.y" { (yyval.val).ctype = CTxxx; } break; case 305: -#line 1920 "go.y" +#line 1922 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; case 306: -#line 1924 "go.y" +#line 1926 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; case 307: -#line 1928 "go.y" +#line 1930 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; case 308: -#line 1932 "go.y" +#line 1934 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; case 309: -#line 1936 "go.y" +#line 1938 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; case 310: -#line 1940 "go.y" +#line 1942 "go.y" { if((yyvsp[(2) - (4)].node) == N) { dclcontext = PEXTERN; // since we skip the funcbody below @@ -4599,7 +4601,7 @@ yyreduce: break; case 311: -#line 1960 "go.y" +#line 1962 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; @@ -4607,7 +4609,7 @@ yyreduce: break; case 312: -#line 1967 "go.y" +#line 1969 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); @@ -4615,14 +4617,14 @@ yyreduce: break; case 318: -#line 1987 "go.y" +#line 1989 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; case 319: -#line 1991 "go.y" +#line 1993 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -4635,49 +4637,49 @@ yyreduce: break; case 320: -#line 2001 "go.y" +#line 2003 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; case 321: -#line 2005 "go.y" +#line 2007 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; case 322: -#line 2009 "go.y" +#line 2011 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; case 323: -#line 2013 "go.y" +#line 2015 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; case 324: -#line 2017 "go.y" +#line 2019 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; case 325: -#line 2021 "go.y" +#line 2023 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; case 326: -#line 2025 "go.y" +#line 2027 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -4686,7 +4688,7 @@ yyreduce: break; case 327: -#line 2031 "go.y" +#line 2033 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -4695,7 +4697,7 @@ yyreduce: break; case 328: -#line 2037 "go.y" +#line 2039 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4704,7 +4706,7 @@ yyreduce: break; case 329: -#line 2045 "go.y" +#line 2047 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -4713,14 +4715,14 @@ yyreduce: break; case 330: -#line 2053 "go.y" +#line 2055 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; case 331: -#line 2059 "go.y" +#line 2061 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -4730,7 +4732,7 @@ yyreduce: break; case 332: -#line 2066 "go.y" +#line 2068 "go.y" { Type *t; @@ -4747,7 +4749,7 @@ yyreduce: break; case 333: -#line 2082 "go.y" +#line 2084 "go.y" { Sym *s; Pkg *p; @@ -4770,49 +4772,49 @@ yyreduce: break; case 334: -#line 2104 "go.y" +#line 2106 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; case 335: -#line 2108 "go.y" +#line 2110 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; case 336: -#line 2113 "go.y" +#line 2115 "go.y" { (yyval.list) = nil; } break; case 338: -#line 2120 "go.y" +#line 2122 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; case 339: -#line 2124 "go.y" +#line 2126 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; case 340: -#line 2134 "go.y" +#line 2136 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 341: -#line 2138 "go.y" +#line 2140 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -4834,7 +4836,7 @@ yyreduce: break; case 342: -#line 2157 "go.y" +#line 2159 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -4843,7 +4845,7 @@ yyreduce: break; case 344: -#line 2166 "go.y" +#line 2168 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); @@ -4857,42 +4859,42 @@ yyreduce: break; case 347: -#line 2182 "go.y" +#line 2184 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 348: -#line 2186 "go.y" +#line 2188 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 349: -#line 2192 "go.y" +#line 2194 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 350: -#line 2196 "go.y" +#line 2198 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 351: -#line 2202 "go.y" +#line 2204 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 352: -#line 2206 "go.y" +#line 2208 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } @@ -4900,7 +4902,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 4905 "y.tab.c" +#line 4907 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -5114,7 +5116,7 @@ yyreturn: } -#line 2210 "go.y" +#line 2212 "go.y" static void diff --git a/test/fixedbugs/issue8311.go b/test/fixedbugs/issue8311.go new file mode 100644 index 0000000000..dd928566d6 --- /dev/null +++ b/test/fixedbugs/issue8311.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2014 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. + +// issue 8311. +// error for x++ should say x++ not x += 1 + +package p + +func f() { + var x []byte + x++ // ERROR "invalid operation: x[+][+]" + +} -- cgit v1.3 From 870f4e190ccbc70900bd0ba724d4f4c1e14e3070 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 13:24:43 -0400 Subject: cmd/gc: emit error for out-of-bounds slice of constant string Fixes #7200. LGTM=gri, iant R=golang-codereviews, gri, iant CC=golang-codereviews, r https://golang.org/cl/150020044 --- src/cmd/gc/typecheck.c | 25 +++++++++++++++---------- test/fixedbugs/issue4232.go | 29 ++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 13 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 9440526060..ff49fe6f92 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -33,7 +33,7 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static void checkdefergo(Node*); static int checkmake(Type*, char*, Node*); -static int checksliceindex(Node*, Type*); +static int checksliceindex(Node*, Node*, Type*); static int checksliceconst(Node*, Node*); static NodeList* typecheckdefstack; @@ -311,6 +311,7 @@ typecheck1(Node **np, int top) Type *t, *tp, *missing, *have, *badtype; Val v; char *why, *desc, descbuf[64]; + vlong x; n = *np; @@ -895,11 +896,12 @@ reswitch: break; } if(isconst(n->right, CTINT)) { - if(mpgetfix(n->right->val.u.xval) < 0) + x = mpgetfix(n->right->val.u.xval); + if(x < 0) yyerror("invalid %s index %N (index must be non-negative)", why, n->right); - else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound) + else if(isfixedarray(t) && t->bound > 0 && x >= t->bound) yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound); - else if(isconst(n->left, CTSTR) && mpgetfix(n->right->val.u.xval) >= n->left->val.u.sval->len) + else if(isconst(n->left, CTSTR) && x >= n->left->val.u.sval->len) yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len); else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) yyerror("invalid %s index %N (index too large)", why, n->right); @@ -999,9 +1001,9 @@ reswitch: yyerror("cannot slice %N (type %T)", l, t); goto error; } - if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0) goto error; - if((hi = n->right->right) != N && checksliceindex(hi, tp) < 0) + if((hi = n->right->right) != N && checksliceindex(l, hi, tp) < 0) goto error; if(checksliceconst(lo, hi) < 0) goto error; @@ -1048,11 +1050,11 @@ reswitch: yyerror("cannot slice %N (type %T)", l, t); goto error; } - if((lo = n->right->left) != N && checksliceindex(lo, tp) < 0) + if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0) goto error; - if((mid = n->right->right->left) != N && checksliceindex(mid, tp) < 0) + if((mid = n->right->right->left) != N && checksliceindex(l, mid, tp) < 0) goto error; - if((hi = n->right->right->right) != N && checksliceindex(hi, tp) < 0) + if((hi = n->right->right->right) != N && checksliceindex(l, hi, tp) < 0) goto error; if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0) goto error; @@ -1822,7 +1824,7 @@ out: } static int -checksliceindex(Node *r, Type *tp) +checksliceindex(Node *l, Node *r, Type *tp) { Type *t; @@ -1839,6 +1841,9 @@ checksliceindex(Node *r, Type *tp) } else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) { yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound); return -1; + } else if(isconst(l, CTSTR) && mpgetfix(r->val.u.xval) > l->val.u.sval->len) { + yyerror("invalid slice index %N (out of bounds for %d-byte string)", r, l->val.u.sval->len); + return -1; } else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) { yyerror("invalid slice index %N (index too large)", r); return -1; diff --git a/test/fixedbugs/issue4232.go b/test/fixedbugs/issue4232.go index e5daa65623..755b1b1de0 100644 --- a/test/fixedbugs/issue4232.go +++ b/test/fixedbugs/issue4232.go @@ -4,6 +4,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// issue 4232 +// issue 7200 + package p func f() { @@ -12,22 +15,42 @@ func f() { _ = a[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = a[:-1] // ERROR "invalid slice index -1|index out of bounds" _ = a[10] // ERROR "invalid array index 10|index out of bounds" + _ = a[9:10] + _ = a[10:10] + _ = a[9:12] // ERROR "invalid slice index 12|index out of bounds" + _ = a[11:12] // ERROR "invalid slice index 11|index out of bounds" + _ = a[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" var s []int _ = s[-1] // ERROR "invalid slice index -1|index out of bounds" _ = s[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = s[:-1] // ERROR "invalid slice index -1|index out of bounds" _ = s[10] + _ = s[9:10] + _ = s[10:10] + _ = s[9:12] + _ = s[11:12] + _ = s[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" - const c = "foo" + const c = "foofoofoof" _ = c[-1] // ERROR "invalid string index -1|index out of bounds" _ = c[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = c[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = c[3] // ERROR "invalid string index 3|index out of bounds" + _ = c[10] // ERROR "invalid string index 10|index out of bounds" + _ = c[9:10] + _ = c[10:10] + _ = c[9:12] // ERROR "invalid slice index 12|index out of bounds" + _ = c[11:12] // ERROR "invalid slice index 11|index out of bounds" + _ = c[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" var t string _ = t[-1] // ERROR "invalid string index -1|index out of bounds" _ = t[-1:] // ERROR "invalid slice index -1|index out of bounds" _ = t[:-1] // ERROR "invalid slice index -1|index out of bounds" - _ = t[3] + _ = t[10] + _ = t[9:10] + _ = t[10:10] + _ = t[9:12] + _ = t[11:12] + _ = t[1<<100 : 1<<110] // ERROR "overflows int" "invalid slice index 1 << 100|index out of bounds" } -- cgit v1.3 From dd84cf4ea0aefe7758dea21a6d123f8283941bf9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 25 Sep 2014 15:57:52 -0400 Subject: cmd/go: install dependencies for 'go build -i' on a command Fixes #8242. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/147150043 --- src/cmd/go/build.go | 11 ++--- src/cmd/go/test.bash | 114 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 47 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index fcc6b699be..c72631ae9e 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -284,6 +284,11 @@ func runBuild(cmd *Command, args []string) { } } + depMode := modeBuild + if buildI { + depMode = modeInstall + } + if *buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") @@ -292,17 +297,13 @@ func runBuild(cmd *Command, args []string) { } p := pkgs[0] p.target = "" // must build - not up to date - a := b.action(modeInstall, modeBuild, p) + a := b.action(modeInstall, depMode, p) a.target = *buildO b.do(a) return } a := &action{} - depMode := modeBuild - if buildI { - depMode = modeInstall - } for _, p := range packages(args) { a.deps = append(a.deps, b.action(modeBuild, depMode, p)) } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 9ae17e1054..8bd01ea21b 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -11,11 +11,15 @@ go() { } started=false +testdesc="" +nl=" +" TEST() { if $started; then stop fi echo TEST: "$@" + testdesc="$@" started=true ok=true } @@ -29,6 +33,7 @@ stop() { echo PASS else echo FAIL + testfail="$testfail $testdesc$nl" allok=false fi } @@ -59,8 +64,8 @@ rm -r $d testlocal() { local="$1" TEST local imports $2 '(easy)' - ./testgo build -o hello "testdata/$local/easy.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/easy.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^easysub\.Hello' hello.out; then echo "testdata/$local/easy.go did not generate expected output" cat hello.out @@ -68,8 +73,8 @@ testlocal() { fi TEST local imports $2 '(easysub)' - ./testgo build -o hello "testdata/$local/easysub/main.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/easysub/main.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^easysub\.Hello' hello.out; then echo "testdata/$local/easysub/main.go did not generate expected output" cat hello.out @@ -77,8 +82,8 @@ testlocal() { fi TEST local imports $2 '(hard)' - ./testgo build -o hello "testdata/$local/hard.go" - ./hello >hello.out + ./testgo build -o hello "testdata/$local/hard.go" || ok=false + ./hello >hello.out || ok=false if ! grep -q '^sub\.Hello' hello.out || ! grep -q '^subsub\.Hello' hello.out ; then echo "testdata/$local/hard.go did not generate expected output" cat hello.out @@ -317,20 +322,20 @@ TEST godoc installs into GOBIN d=$(mktemp -d -t testgoXXX) export GOPATH=$d mkdir $d/gobin -GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc +GOBIN=$d/gobin ./testgo get code.google.com/p/go.tools/cmd/godoc || ok=false if [ ! -x $d/gobin/godoc ]; then echo did not install godoc to '$GOBIN' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true ok=false fi TEST godoc installs into GOROOT GOROOT=$(./testgo env GOROOT) rm -f $GOROOT/bin/godoc -./testgo install code.google.com/p/go.tools/cmd/godoc +./testgo install code.google.com/p/go.tools/cmd/godoc || ok=false if [ ! -x $GOROOT/bin/godoc ]; then echo did not install godoc to '$GOROOT/bin' - ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc + ./testgo list -f 'Target: {{.Target}}' code.google.com/p/go.tools/cmd/godoc || true ok=false fi @@ -338,36 +343,36 @@ TEST cmd/fix installs into tool GOOS=$(./testgo env GOOS) GOARCH=$(./testgo env GOARCH) rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -./testgo install cmd/fix +./testgo install cmd/fix || ok=false if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then echo 'did not install cmd/fix to $GOROOT/pkg/tool' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true ok=false fi rm -f $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix -GOBIN=$d/gobin ./testgo install cmd/fix +GOBIN=$d/gobin ./testgo install cmd/fix || ok=false if [ ! -x $GOROOT/pkg/tool/${GOOS}_${GOARCH}/fix ]; then echo 'did not install cmd/fix to $GOROOT/pkg/tool with $GOBIN set' - GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix + GOBIN=$d/gobin ./testgo list -f 'Target: {{.Target}}' cmd/fix || true ok=false fi TEST gopath program installs into GOBIN mkdir $d/src/progname echo 'package main; func main() {}' >$d/src/progname/p.go -GOBIN=$d/gobin ./testgo install progname +GOBIN=$d/gobin ./testgo install progname || ok=false if [ ! -x $d/gobin/progname ]; then echo 'did not install progname to $GOBIN/progname' - ./testgo list -f 'Target: {{.Target}}' cmd/api + ./testgo list -f 'Target: {{.Target}}' cmd/api || true ok=false fi rm -f $d/gobin/progname $d/bin/progname TEST gopath program installs into GOPATH/bin -./testgo install progname +./testgo install progname || ok=false if [ ! -x $d/bin/progname ]; then echo 'did not install progname to $GOPATH/bin/progname' - ./testgo list -f 'Target: {{.Target}}' progname + ./testgo list -f 'Target: {{.Target}}' progname || true ok=false fi @@ -396,7 +401,7 @@ fi # ensure that output of 'go list' is consistent between runs TEST go list is consistent -./testgo list std > test_std.list +./testgo list std > test_std.list || ok=false if ! ./testgo list std | cmp -s test_std.list - ; then echo "go list std ordering is inconsistent" ok=false @@ -470,7 +475,7 @@ func main() { println(extern) } EOF -./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out +./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out || ok=false if ! grep -q '^hello world' hello.out; then echo "ldflags -X main.extern 'hello world' failed. Output:" cat hello.out @@ -608,28 +613,36 @@ TEST shadowing logic export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 # The math in root1 is not "math" because the standard math is. +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/math) +set -e if [ "$cdir" != "(_$(pwd)/testdata/shadow/root1/src/math) ($GOROOT/src/math)" ]; then echo shadowed math is not shadowed: "$cdir" ok=false fi # The foo in root1 is "foo". +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root1/src/foo) +set -e if [ "$cdir" != "(foo) ()" ]; then echo unshadowed foo is shadowed: "$cdir" ok=false fi # The foo in root2 is not "foo" because the foo in root1 got there first. +set +e cdir=$(./testgo list -f '({{.ImportPath}}) ({{.ConflictDir}})' ./testdata/shadow/root2/src/foo) +set -e if [ "$cdir" != "(_$(pwd)/testdata/shadow/root2/src/foo) ($(pwd)/testdata/shadow/root1/src/foo)" ]; then echo shadowed foo is not shadowed: "$cdir" ok=false fi # The error for go install should mention the conflicting directory. -err=$(! ./testgo install ./testdata/shadow/root2/src/foo 2>&1) +set +e +err=$(./testgo install ./testdata/shadow/root2/src/foo 2>&1) +set -e if [ "$err" != "go install: no install location for $(pwd)/testdata/shadow/root2/src/foo: hidden by $(pwd)/testdata/shadow/root1/src/foo" ]; then echo wrong shadowed install error: "$err" ok=false @@ -818,30 +831,46 @@ echo ' package foo func F() {} ' >$d/src/x/y/foo/foo.go +checkbar() { + desc="$1" + sleep 1 + touch $d/src/x/y/foo/foo.go + if ! ./testgo build -v -i x/y/bar &> $d/err; then + echo build -i "$1" failed + cat $d/err + ok=false + elif ! grep x/y/foo $d/err >/dev/null; then + echo first build -i "$1" did not build x/y/foo + cat $d/err + ok=false + fi + if ! ./testgo build -v -i x/y/bar &> $d/err; then + echo second build -i "$1" failed + cat $d/err + ok=false + elif grep x/y/foo $d/err >/dev/null; then + echo second build -i "$1" built x/y/foo + cat $d/err + ok=false + fi +} + echo ' package bar import "x/y/foo" func F() { foo.F() } ' >$d/src/x/y/bar/bar.go -if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo build -i failed - cat $d/err - ok=false -elif ! grep x/y/foo $d/err >/dev/null; then - echo first build -i did not build x/y/foo - cat $d/err - ok=false -fi -if ! ./testgo build -v -i x/y/bar &> $d/err; then - echo second build -i failed - cat $d/err - ok=false -elif grep x/y/foo $d/err >/dev/null; then - echo second build -i built x/y/foo - cat $d/err - ok=false -fi -rm -rf $d +checkbar pkg + +TEST build -i installs dependencies for command +echo ' +package main +import "x/y/foo" +func main() { foo.F() } +' >$d/src/x/y/bar/bar.go +checkbar cmd + +rm -rf $d bar unset GOPATH TEST 'go build in test-only directory fails with a good error' @@ -876,7 +905,7 @@ fi TEST 'go test xtestonly works' export GOPATH=$(pwd)/testdata -./testgo clean -i xtestonly +./testgo clean -i xtestonly || ok=false if ! ./testgo test xtestonly >/dev/null; then echo "go test xtestonly failed" ok=false @@ -927,6 +956,7 @@ rm -f testgo if $allok; then echo PASS else - echo FAIL + echo FAIL: + echo "$testfail" exit 1 fi -- cgit v1.3 From 7fa96f08a4b11d710c8b1cb0d8f29e131e1793db Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 12:09:27 -0400 Subject: cmd/go: fix 'go get vanity/repo/...' in clean GOPATH The pattern was only working if the checkout had already been done, but the code was trying to make it work even the first time. Test and fix. Fixes #8335. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/146310043 --- src/cmd/go/test.bash | 12 ++++++++++++ src/cmd/go/vcs.go | 9 ++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/cmd') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 8bd01ea21b..5e4e43722a 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -948,6 +948,18 @@ elif ! grep "$GOARCH test3.go p xyzp/test3.go/123" testdata/std.out > /dev/null; ok=false fi +TEST go get works with vanity wildcards +d=$(mktemp -d -t testgoXXX) +export GOPATH=$d +if ! ./testgo get -u rsc.io/pdf/...; then + ok=false +elif [ ! -x $d/bin/pdfpasswd ]; then + echo did not build rsc.io/pdf/pdfpasswd + ok=false +fi +unset GOPATH +rm -rf $d + # clean up if $started; then stop; fi rm -rf testdata/bin testdata/bin1 diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index d07948e64c..c5d246835d 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -361,7 +361,14 @@ var httpPrefixRE = regexp.MustCompile(`^https?:`) func repoRootForImportPath(importPath string) (*repoRoot, error) { rr, err := repoRootForImportPathStatic(importPath, "") if err == errUnknownSite { - rr, err = repoRootForImportDynamic(importPath) + // If there are wildcards, look up the thing before the wildcard, + // hoping it applies to the wildcarded parts too. + // This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH. + lookup := strings.TrimSuffix(importPath, "/...") + if i := strings.Index(lookup, "/.../"); i >= 0 { + lookup = lookup[:i] + } + rr, err = repoRootForImportDynamic(lookup) // repoRootForImportDynamic returns error detail // that is irrelevant if the user didn't intend to use a -- cgit v1.3 From f3a98dee27540afab414d3201aff18f30c2b163e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 12:10:13 -0400 Subject: cmd/go: re-resolve and check vcs roots during go get -u If you do 'go get -u rsc.io/pdf' and then rsc.io/pdf's redirect changes to point somewhere else, after this CL a later 'go get -u rsc.io/pdf' will tell you that. Fixes #8548. LGTM=iant R=golang-codereviews, iant CC=adg, golang-codereviews, n13m3y3r, r https://golang.org/cl/147170043 --- src/cmd/go/get.go | 12 ++++++++ src/cmd/go/test.bash | 50 +++++++++++++++++++++++++++++++++ src/cmd/go/vcs.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 135 insertions(+), 6 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index a34286f540..2640339414 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -266,6 +266,18 @@ func downloadPackage(p *Package) error { return err } repo = "" // should be unused; make distinctive + + // Double-check where it came from. + if *getU && vcs.remoteRepo != nil { + dir := filepath.Join(p.build.SrcRoot, rootPath) + if remote, err := vcs.remoteRepo(vcs, dir); err == nil { + if rr, err := repoRootForImportPath(p.ImportPath); err == nil { + if remote != rr.repo { + return fmt.Errorf("%s is from %s, should be from %s", dir, remote, rr.repo) + } + } + } + } } else { // Analyze the import path to determine the version control system, // repository, and the import path for the root of the repository. diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 5e4e43722a..243467ba9d 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -126,6 +126,56 @@ if ! ./testgo build -v ./testdata/testinternal2; then ok=false fi +# Test that 'go get -u' reports moved packages. +testmove() { + vcs=$1 + url=$2 + base=$3 + config=$4 + + TEST go get -u notices $vcs package that moved + d=$(mktemp -d -t testgoXXX) + mkdir -p $d/src + if ! GOPATH=$d ./testgo get -d $url; then + echo 'go get -d $url failed' + ok=false + elif ! GOPATH=$d ./testgo get -d -u $url; then + echo 'go get -d -u $url failed' + ok=false + else + set +e + case "$vcs" in + svn) + # SVN doesn't believe in text files so we can't just edit the config. + # Check out a different repo into the wrong place. + rm -rf $d/src/code.google.com/p/rsc-svn + GOPATH=$d ./testgo get -d -u code.google.com/p/rsc-svn2/trunk + mv $d/src/code.google.com/p/rsc-svn2 $d/src/code.google.com/p/rsc-svn + ;; + *) + echo '1,$s;'"$base"';'"$base"'XXX; +w +q' | ed $d/src/$config >/dev/null 2>&1 + esac + set -e + + if GOPATH=$d ./testgo get -d -u $url 2>$d/err; then + echo "go get -d -u $url succeeded with wrong remote repo" + cat $d/err + ok=false + elif ! grep 'should be from' $d/err >/dev/null; then + echo "go get -d -u $url failed for wrong reason" + cat $d/err + ok=false + fi + fi + rm -rf $d +} + +testmove hg rsc.io/x86/x86asm x86 rsc.io/x86/.hg/hgrc +testmove git rsc.io/pdf pdf rsc.io/pdf/.git/config +testmove svn code.google.com/p/rsc-svn/trunk - - + export GOPATH=$(pwd)/testdata/importcom TEST 'import comment - match' if ! ./testgo build ./testdata/importcom/works.go; then diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index c5d246835d..103b67b827 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -33,6 +33,8 @@ type vcsCmd struct { scheme []string pingCmd string + + remoteRepo func(v *vcsCmd, rootDir string) (remoteRepo string, err error) } // A tagCmd describes a command to list available tags @@ -81,8 +83,17 @@ var vcsHg = &vcsCmd{ tagSyncCmd: "update -r {tag}", tagSyncDefault: "update default", - scheme: []string{"https", "http", "ssh"}, - pingCmd: "identify {scheme}://{repo}", + scheme: []string{"https", "http", "ssh"}, + pingCmd: "identify {scheme}://{repo}", + remoteRepo: hgRemoteRepo, +} + +func hgRemoteRepo(vcsHg *vcsCmd, rootDir string) (remoteRepo string, err error) { + out, err := vcsHg.runOutput(rootDir, "paths default") + if err != nil { + return "", err + } + return strings.TrimSpace(string(out)), nil } // vcsGit describes how to use Git. @@ -104,8 +115,38 @@ var vcsGit = &vcsCmd{ tagSyncCmd: "checkout {tag}", tagSyncDefault: "checkout master", - scheme: []string{"git", "https", "http", "git+ssh"}, - pingCmd: "ls-remote {scheme}://{repo}", + scheme: []string{"git", "https", "http", "git+ssh"}, + pingCmd: "ls-remote {scheme}://{repo}", + remoteRepo: gitRemoteRepo, +} + +func gitRemoteRepo(vcsGit *vcsCmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsGit.runOutput(rootDir, "remote -v") + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // origin https://github.com/rsc/pdf (fetch) + // origin https://github.com/rsc/pdf (push) + // use first line only. + + if !strings.HasPrefix(out, "origin\t") { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = strings.TrimPrefix(out, "origin\t") + i := strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = out[:i] + i = strings.LastIndex(out, " ") + if i < 0 { + return "", fmt.Errorf("unable to parse output of git remote -v") + } + out = out[:i] + return strings.TrimSpace(string(out)), nil } // vcsBzr describes how to use Bazaar. @@ -138,8 +179,34 @@ var vcsSvn = &vcsCmd{ // There is no tag command in subversion. // The branch information is all in the path names. - scheme: []string{"https", "http", "svn", "svn+ssh"}, - pingCmd: "info {scheme}://{repo}", + scheme: []string{"https", "http", "svn", "svn+ssh"}, + pingCmd: "info {scheme}://{repo}", + remoteRepo: svnRemoteRepo, +} + +func svnRemoteRepo(vcsSvn *vcsCmd, rootDir string) (remoteRepo string, err error) { + outb, err := vcsSvn.runOutput(rootDir, "info") + if err != nil { + return "", err + } + out := string(outb) + + // Expect: + // ... + // Repository Root: + // ... + + i := strings.Index(out, "\nRepository Root: ") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[i+len("\nRepository Root: "):] + i = strings.Index(out, "\n") + if i < 0 { + return "", fmt.Errorf("unable to parse output of svn info") + } + out = out[:i] + return strings.TrimSpace(string(out)), nil } func (v *vcsCmd) String() string { -- cgit v1.3 From b86105e80da7fd994656f134d006a700fe037fc2 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:47:51 -0400 Subject: cmd/go: make malformed import path message more precise If you say 'go get -v' you get extra information when import paths are not of the expected form. If you say 'go get -v src/rsc.io/pdf' the message says that src/rsc.io/pdf does not contain a hostname, which is incorrect. The problem is that it does not begin with a hostname. Fixes #7432. LGTM=r R=golang-codereviews, r CC=bradfitz, golang-codereviews, iant https://golang.org/cl/144650043 --- src/cmd/go/vcs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 103b67b827..0834a7d192 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -539,11 +539,11 @@ func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) { func repoRootForImportDynamic(importPath string) (*repoRoot, error) { slash := strings.Index(importPath, "/") if slash < 0 { - return nil, errors.New("import path doesn't contain a slash") + return nil, errors.New("import path does not contain a slash") } host := importPath[:slash] if !strings.Contains(host, ".") { - return nil, errors.New("import path doesn't contain a hostname") + return nil, errors.New("import path does not begin with hostname") } urlStr, body, err := httpsOrHTTP(importPath) if err != nil { -- cgit v1.3 From 13a2c1ca78c47a4314e32badddf6c31c4229da7f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:48:30 -0400 Subject: cmd/go: display program name when reporting crash Fix by atom (from CL 89190044), comment and test by me. Fixes #6823. LGTM=crawshaw R=golang-codereviews, crawshaw CC=0xe2.0x9a.0x9b, adg, golang-codereviews, iant, r https://golang.org/cl/148180043 --- src/cmd/go/build.go | 8 ++++++++ src/cmd/go/test.bash | 11 +++++++++++ src/cmd/ld/pobj.c | 9 +++++++++ 3 files changed, 28 insertions(+) (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index c72631ae9e..e2e17fd036 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -1469,6 +1469,14 @@ func (b *builder) runOut(dir string, desc string, env []string, cmdargs ...inter continue } + // err can be something like 'exit status 1'. + // Add information about what program was running. + // Note that if buf.Bytes() is non-empty, the caller usually + // shows buf.Bytes() and does not print err at all, so the + // prefix here does not make most output any more verbose. + if err != nil { + err = errors.New(cmdline[0] + ": " + err.Error()) + } return buf.Bytes(), err } } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 243467ba9d..80af61ae0e 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -60,6 +60,17 @@ if ! grep -q "^$fn:" $d/err.out; then fi rm -r $d +TEST 'program name in crash messages' +linker=$(./testgo env GOCHAR)l +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +./testgo build -ldflags -crash_for_testing $(./testgo env GOROOT)/test/helloworld.go 2>$d/err.out || true +if ! grep -q "/tool/.*/$linker" $d/err.out; then + echo "missing linker name in error message" + cat $d/err.out + ok=false +fi +rm -r $d + # Test local (./) imports. testlocal() { local="$1" diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c index 54c5ef2472..63460df30a 100644 --- a/src/cmd/ld/pobj.c +++ b/src/cmd/ld/pobj.c @@ -45,6 +45,8 @@ char* paramspace = "FP"; void main(int argc, char *argv[]) { + int i; + linkarchinit(); ctxt = linknew(thelinkarch); ctxt->thechar = thechar; @@ -64,6 +66,13 @@ main(int argc, char *argv[]) INITENTRY = 0; linkmode = LinkAuto; + // For testing behavior of go command when tools crash. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for(i=1; igoarm == 5) debug['F'] = 1; -- cgit v1.3 From ce143f25e625ce40f4655185372dc93661545df0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:50:02 -0400 Subject: cmd/go: add test -o flag to control where test binary is written While we are here, remove undocumented, meaningless test -file flag. Fixes #7724. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/149070043 --- src/cmd/go/test.bash | 24 +++++++++++++++++++++++ src/cmd/go/test.go | 52 +++++++++++++++++++++++++++++++++----------------- src/cmd/go/testflag.go | 7 ++++--- 3 files changed, 63 insertions(+), 20 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 80af61ae0e..bc2ce710a0 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -552,6 +552,30 @@ if [ ! -x strings.test ]; then fi rm -f strings.prof strings.test +TEST go test -cpuprofile -o controls binary location +./testgo test -cpuprofile strings.prof -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -cpuprofile -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f strings.prof mystrings.test + +TEST go test -c -o controls binary location +./testgo test -c -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -c -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f mystrings.test + +TEST go test -o writes binary +./testgo test -o mystrings.test strings || ok=false +if [ ! -x mystrings.test ]; then + echo "go test -o mystrings.test did not create mystrings.test" + ok=false +fi +rm -f mystrings.test + TEST symlinks do not confuse go list '(issue 4568)' old=$(pwd) tmp=$(cd /tmp && pwd -P) diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index c135b89c84..100ef5fa82 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -66,16 +66,23 @@ non-test installation. In addition to the build flags, the flags handled by 'go test' itself are: - -c Compile the test binary to pkg.test but do not run it. - (Where pkg is the last element of the package's import path.) + -c + Compile the test binary to pkg.test but do not run it + (where pkg is the last element of the package's import path). + The file name can be changed with the -o flag. + + -exec xprog + Run the test binary using xprog. The behavior is the same as + in 'go run'. See 'go help run' for details. -i Install packages that are dependencies of the test. Do not run the test. - -exec xprog - Run the test binary using xprog. The behavior is the same as - in 'go run'. See 'go help run' for details. + -o file + Compile the test binary to the named file. + The test still runs (unless -c or -i is specified). + The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. @@ -123,6 +130,7 @@ control the execution of any test: -blockprofile block.out Write a goroutine blocking profile to the specified file when all tests are complete. + Writes test binary as -c would. -blockprofilerate n Control the detail provided in goroutine blocking profiles by @@ -154,8 +162,7 @@ control the execution of any test: Sets -cover. -coverprofile cover.out - Write a coverage profile to the specified file after all tests - have passed. + Write a coverage profile to the file after all tests have passed. Sets -cover. -cpu 1,2,4 @@ -165,10 +172,11 @@ control the execution of any test: -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. + Writes test binary as -c would. -memprofile mem.out - Write a memory profile to the specified file after all tests - have passed. + Write a memory profile to the file after all tests have passed. + Writes test binary as -c would. -memprofilerate n Enable more precise (and expensive) memory profiles by setting @@ -275,10 +283,10 @@ var ( testCoverMode string // -covermode flag testCoverPaths []string // -coverpkg flag testCoverPkgs []*Package // -coverpkg flag + testO string // -o flag testProfile bool // some profiling flag testNeedBinary bool // profile needs to keep binary around testV bool // -v flag - testFiles []string // -file flag(s) TODO: not respected testTimeout string // -timeout flag testArgs []string testBench bool @@ -310,6 +318,9 @@ func runTest(cmd *Command, args []string) { if testC && len(pkgs) != 1 { fatalf("cannot use -c flag with multiple packages") } + if testO != "" && len(pkgs) != 1 { + fatalf("cannot use -o flag with multiple packages") + } if testProfile && len(pkgs) != 1 { fatalf("cannot use test profile flag with multiple packages") } @@ -781,17 +792,24 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.objdir = testDir + string(filepath.Separator) a.objpkg = filepath.Join(testDir, "main.a") a.target = filepath.Join(testDir, testBinary) + exeSuffix - pmainAction := a + buildAction = a if testC || testNeedBinary { // -c or profiling flag: create action to copy binary to ./test.out. - runAction = &action{ + target := filepath.Join(cwd, testBinary+exeSuffix) + if testO != "" { + target = testO + if !filepath.IsAbs(target) { + target = filepath.Join(cwd, target) + } + } + buildAction = &action{ f: (*builder).install, - deps: []*action{pmainAction}, + deps: []*action{buildAction}, p: pmain, - target: filepath.Join(cwd, testBinary+exeSuffix), + target: target, } - pmainAction = runAction // in case we are running the test + runAction = buildAction // make sure runAction != nil even if not running test } if testC { printAction = &action{p: p, deps: []*action{runAction}} // nop @@ -799,7 +817,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // run test runAction = &action{ f: (*builder).runTest, - deps: []*action{pmainAction}, + deps: []*action{buildAction}, p: p, ignoreFail: true, } @@ -815,7 +833,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, } } - return pmainAction, runAction, printAction, nil + return buildAction, runAction, printAction, nil } func testImportStack(top string, p *Package, target string) []string { diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index 73f311e5f6..6da74b9967 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -65,9 +65,9 @@ type testFlagSpec struct { var testFlagDefn = []*testFlagSpec{ // local. {name: "c", boolVar: &testC}, - {name: "file", multiOK: true}, {name: "cover", boolVar: &testCover}, {name: "coverpkg"}, + {name: "o"}, // build flags. {name: "a", boolVar: &buildA}, @@ -153,6 +153,9 @@ func testFlags(args []string) (packageNames, passToTest []string) { // bool flags. case "a", "c", "i", "n", "x", "v", "race", "cover", "work": setBoolFlag(f.boolVar, value) + case "o": + testO = value + testNeedBinary = true case "p": setIntFlag(&buildP, value) case "exec": @@ -184,8 +187,6 @@ func testFlags(args []string) (packageNames, passToTest []string) { buildContext.BuildTags = strings.Fields(value) case "compiler": buildCompiler{}.Set(value) - case "file": - testFiles = append(testFiles, value) case "bench": // record that we saw the flag; don't care about the value testBench = true -- cgit v1.3 From 6d760fb082544531bd14be27b438a1d3a1ec0016 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 13:50:39 -0400 Subject: cmd/go: document that testdata directories are ignored Also rebuild doc.go; was stale, so contains extra changes. Fixes #8677. LGTM=r R=golang-codereviews, r CC=golang-codereviews, iant https://golang.org/cl/148170043 --- src/cmd/go/doc.go | 29 +++++++++++++++++++---------- src/cmd/go/help.go | 3 ++- 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 0d4e263891..cffb53d995 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -524,16 +524,23 @@ non-test installation. In addition to the build flags, the flags handled by 'go test' itself are: - -c Compile the test binary to pkg.test but do not run it. - (Where pkg is the last element of the package's import path.) + -c + Compile the test binary to pkg.test but do not run it + (where pkg is the last element of the package's import path). + The file name can be changed with the -o flag. + + -exec xprog + Run the test binary using xprog. The behavior is the same as + in 'go run'. See 'go help run' for details. -i Install packages that are dependencies of the test. Do not run the test. - -exec xprog - Run the test binary using xprog. The behavior is the same as - in 'go run'. See 'go help run' for details. + -o file + Compile the test binary to the named file. + The test still runs (unless -c or -i is specified). + The test binary also accepts flags that control execution of the test; these flags are also accessible by 'go test'. See 'go help testflag' for details. @@ -910,7 +917,8 @@ single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory. -File names that begin with "." or "_" are ignored by the go tool. +Directory and file names that begin with "." or "_" are ignored +by the go tool, as are directories named "testdata". Description of testing flags @@ -942,6 +950,7 @@ control the execution of any test: -blockprofile block.out Write a goroutine blocking profile to the specified file when all tests are complete. + Writes test binary as -c would. -blockprofilerate n Control the detail provided in goroutine blocking profiles by @@ -973,8 +982,7 @@ control the execution of any test: Sets -cover. -coverprofile cover.out - Write a coverage profile to the specified file after all tests - have passed. + Write a coverage profile to the file after all tests have passed. Sets -cover. -cpu 1,2,4 @@ -984,10 +992,11 @@ control the execution of any test: -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. + Writes test binary as -c would. -memprofile mem.out - Write a memory profile to the specified file after all tests - have passed. + Write a memory profile to the file after all tests have passed. + Writes test binary as -c would. -memprofilerate n Enable more precise (and expensive) memory profiles by setting diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index d6651d179b..201f0e2d79 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -81,7 +81,8 @@ single directory, the command is applied to a single synthesized package made up of exactly those files, ignoring any build constraints in those files and ignoring any other files in the directory. -File names that begin with "." or "_" are ignored by the go tool. +Directory and file names that begin with "." or "_" are ignored +by the go tool, as are directories named "testdata". `, } -- cgit v1.3 From 0b8bc7cee9cc8c2bedc030e9a59a687201210212 Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Fri, 26 Sep 2014 14:36:49 -0400 Subject: cmd/go: handle paths like \x.go on windows Fixes #8130. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/143200043 --- src/cmd/go/build.go | 9 +++++-- src/cmd/go/go_windows_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/go_windows_test.go (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index e2e17fd036..23ad765ba7 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -505,8 +505,13 @@ func goFilesPackage(gofiles []string) *Package { } ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil } - if !filepath.IsAbs(dir) { - dir = filepath.Join(cwd, dir) + var err error + if dir == "" { + dir = cwd + } + dir, err = filepath.Abs(dir) + if err != nil { + fatalf("%s", err) } bp, err := ctxt.ImportDir(dir, 0) diff --git a/src/cmd/go/go_windows_test.go b/src/cmd/go/go_windows_test.go new file mode 100644 index 0000000000..53d695cccc --- /dev/null +++ b/src/cmd/go/go_windows_test.go @@ -0,0 +1,55 @@ +// Copyright 2014 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 main + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestAbsolutePath(t *testing.T) { + tmp, err := ioutil.TempDir("", "TestAbsolutePath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + file := filepath.Join(tmp, "a.go") + err = ioutil.WriteFile(file, []byte{}, 0644) + if err != nil { + t.Fatal(err) + } + dir := filepath.Join(tmp, "dir") + err = os.Mkdir(dir, 0777) + if err != nil { + t.Fatal(err) + } + + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + defer os.Chdir(wd) + + // Chdir so current directory and a.go reside on the same drive. + err = os.Chdir(dir) + if err != nil { + t.Fatal(err) + } + + noVolume := file[len(filepath.VolumeName(file)):] + wrongPath := filepath.Join(dir, noVolume) + output, err := exec.Command("go", "build", noVolume).CombinedOutput() + if err == nil { + t.Fatal("build should fail") + } + if strings.Contains(string(output), wrongPath) { + t.Fatalf("wrong output found: %v %v", err, string(output)) + } +} -- cgit v1.3 From 8c3005c4929e26cba74c80703123e150fa3fda1a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 14:41:26 -0400 Subject: cmd/go: make build -a skip standard packages in Go releases Today, 'go build -a my/pkg' and 'go install -a my/pkg' recompile not just my/pkg and all its dependencies that you wrote but also the standard library packages. Recompiling the standard library is problematic on some systems because the installed copy is not writable. The -a behavior means that you can't use 'go install -a all' or 'go install -a my/...' to rebuild everything after a Go release - the rebuild stops early when it cannot overwrite the installed standard library. During development work, however, you do want install -a to rebuild everything, because anything might have changed. Resolve the conflict by making the behavior of -a depend on whether we are using a released copy of Go or a devel copy. In the release copies, -a no longer applies to the standard library. In the devel copies, it still does. This is the latest in a long line of refinements to the "do I build this or not" logic. It is surely not the last. Fixes #8290. LGTM=r R=golang-codereviews, r, tracey.brendan CC=adg, golang-codereviews, iant https://golang.org/cl/151730045 --- src/cmd/go/build.go | 1 + src/cmd/go/doc.go | 1 + src/cmd/go/pkg.go | 18 +++++++++++++++++- src/cmd/go/test.bash | 28 +++++++++++++++++++++++++++- src/cmd/go/testgo.go | 21 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/testgo.go (limited to 'src/cmd') diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 23ad765ba7..9c7b42650a 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -57,6 +57,7 @@ and test commands: -a force rebuilding of packages that are already up-to-date. + In Go releases, does not apply to the standard library. -n print the commands but do not run them. -p n diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index cffb53d995..8e2facd044 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -76,6 +76,7 @@ and test commands: -a force rebuilding of packages that are already up-to-date. + In Go releases, does not apply to the standard library. -n print the commands but do not run them. -p n diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 4bbcc2b971..eafaa8ee67 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -14,6 +14,7 @@ import ( "os" pathpkg "path" "path/filepath" + "runtime" "sort" "strings" "time" @@ -685,6 +686,12 @@ func computeStale(pkgs ...*Package) { } } +// The runtime version string takes one of two forms: +// "go1.X[.Y]" for Go releases, and "devel +hash" at tip. +// Determine whether we are in a released copy by +// inspecting the version. +var isGoRelease = !strings.HasPrefix(runtime.Version(), "go1") + // isStale reports whether package p needs to be rebuilt. func isStale(p *Package, topRoot map[string]bool) bool { if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { @@ -705,7 +712,16 @@ func isStale(p *Package, topRoot map[string]bool) bool { return false } - if buildA || p.target == "" || p.Stale { + // If we are running a release copy of Go, do not rebuild the standard packages. + // They may not be writable anyway, but they are certainly not changing. + // This makes 'go build -a' skip the standard packages when using an official release. + // See issue 4106 and issue 8290. + pkgBuildA := buildA + if p.Standard && isGoRelease { + pkgBuildA = false + } + + if pkgBuildA || p.target == "" || p.Stale { return true } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index bc2ce710a0..1284876193 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -4,7 +4,7 @@ # license that can be found in the LICENSE file. set -e -go build -o testgo +go build -tags testgo -o testgo go() { echo TEST ERROR: ran go, not testgo: go "$@" >&2 exit 2 @@ -71,6 +71,32 @@ if ! grep -q "/tool/.*/$linker" $d/err.out; then fi rm -r $d +TEST 'go build -a in dev branch' +./testgo install math || ok=false # should be up to date already but just in case +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +if ! TESTGO_IS_GO_RELEASE=0 ./testgo build -v -a math 2>$d/err.out; then + cat $d/err.out + ok=false +elif ! grep -q runtime $d/err.out; then + echo "testgo build -a math in dev branch DID NOT build runtime, but should have" + cat $d/err.out + ok=false +fi +rm -r $d + +TEST 'go build -a in release branch' +./testgo install math || ok=false # should be up to date already but just in case +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +if ! TESTGO_IS_GO_RELEASE=1 ./testgo build -v -a math 2>$d/err.out; then + cat $d/err.out + ok=false +elif grep -q runtime $d/err.out; then + echo "testgo build -a math in dev branch DID build runtime, but should NOT have" + cat $d/err.out + ok=false +fi +rm -r $d + # Test local (./) imports. testlocal() { local="$1" diff --git a/src/cmd/go/testgo.go b/src/cmd/go/testgo.go new file mode 100644 index 0000000000..01923f74bd --- /dev/null +++ b/src/cmd/go/testgo.go @@ -0,0 +1,21 @@ +// Copyright 2014 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. + +// This file contains extra hooks for testing the go command. +// It is compiled into the Go binary only when building the +// test copy; it does not get compiled into the standard go +// command, so these testing hooks are not present in the +// go command that everyone uses. + +// +build testgo + +package main + +import "os" + +func init() { + if v := os.Getenv("TESTGO_IS_GO_RELEASE"); v != "" { + isGoRelease = v == "1" + } +} -- cgit v1.3 From 1bf18b42f8475db2af1618d798285ed84a8dd521 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 15:15:48 -0400 Subject: cmd/go: fix -a The one line that you can't test easily was broken. This manifested as a failure of a pre-existing test in test.bash but I didn't notice it (there are a few other long-standing failures that need to be fixed). TBR=r CC=golang-codereviews https://golang.org/cl/146340044 --- src/cmd/go/pkg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cmd') diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index eafaa8ee67..7f7a3b04fd 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -690,7 +690,7 @@ func computeStale(pkgs ...*Package) { // "go1.X[.Y]" for Go releases, and "devel +hash" at tip. // Determine whether we are in a released copy by // inspecting the version. -var isGoRelease = !strings.HasPrefix(runtime.Version(), "go1") +var isGoRelease = strings.HasPrefix(runtime.Version(), "go1") // isStale reports whether package p needs to be rebuilt. func isStale(p *Package, topRoot map[string]bool) bool { -- cgit v1.3 From bfebf9ea8071683af608b8bf291fc7d8365d501b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 17:03:31 -0400 Subject: cmd/yacc: fix parsing of character tokens From issue 7967 I learned: 1) yacc accepts either 'x' or "x" to mean token value 0x78 2) yacc also accepts 'xyz' and "XYZ" to mean token value 0x78 Use strconv.Unquote to simplify the handling of quoted strings and check that each has only one rune. Although this does clean things up, it makes 'x' and "x" treated as different internally (now they are stored as `'x'` and `"x"`; before they were both ` x`). Grammars that use both interchangeably will now die with an error similar to the one from issue 7967: yacc bug -- cannot have 2 different Ts with same value "+" and '+' The echoing of the quotes should make clear what is going on. The other semantic change caused by using strconv.Unquote is that '\"' and "\'" are no longer valid. Like in Go, they must be spelled without the backslash: '"' and "'". On the other hand, now yacc and Go agree about what character and string literals mean. LGTM=r R=r CC=golang-codereviews https://golang.org/cl/149110043 --- src/cmd/yacc/yacc.go | 76 +++++++++------------------------------------------- 1 file changed, 13 insertions(+), 63 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index c53403266e..0761811cf4 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -52,9 +52,9 @@ import ( "go/format" "io/ioutil" "os" + "strconv" "strings" "unicode" - "unicode/utf8" ) // the following are adjustable @@ -756,64 +756,16 @@ func defin(nt int, s string) int { // establish value for token // single character literal - if s[0] == ' ' { - s = s[1:] - r, size := utf8.DecodeRuneInString(s) - if r == utf8.RuneError && size == 1 { - errorf("invalid UTF-8 sequence %q", s) - } - val = int(r) - if val == '\\' { // escape sequence - switch { - case len(s) == 2: - // single character escape sequence - switch s[1] { - case '\'': - val = '\'' - case '"': - val = '"' - case '\\': - val = '\\' - case 'a': - val = '\a' - case 'b': - val = '\b' - case 'f': - val = '\f' - case 'n': - val = '\n' - case 'r': - val = '\r' - case 't': - val = '\t' - case 'v': - val = '\v' - default: - errorf("invalid escape %s", s) - } - case s[1] == 'u' && len(s) == 2+4, // \unnnn sequence - s[1] == 'U' && len(s) == 2+8: // \Unnnnnnnn sequence - val = 0 - s = s[2:] - for s != "" { - c := int(s[0]) - switch { - case c >= '0' && c <= '9': - c -= '0' - case c >= 'a' && c <= 'f': - c -= 'a' - 10 - case c >= 'A' && c <= 'F': - c -= 'A' - 10 - default: - errorf(`illegal \u or \U construction`) - } - val = val*16 + c - s = s[1:] - } - default: - errorf("invalid escape %s", s) - } + if s[0] == '\'' || s[0] == '"' { + q, err := strconv.Unquote(s) + if err != nil { + errorf("invalid token: %s", err) + } + rq := []rune(q) + if len(rq) != 1 { + errorf("character token too long: %s", s) } + val = int(rq[0]) if val == 0 { errorf("token value 0 is illegal") } @@ -896,7 +848,7 @@ func gettok() int { case '"', '\'': match = c - tokname = " " + tokname = string(c) for { c = getrune(finput) if c == '\n' || c == EOF { @@ -909,6 +861,7 @@ func gettok() int { if tokflag { fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno) } + tokname += string(c) return IDENTIFIER } tokname += string(c) @@ -1029,7 +982,7 @@ func fdtype(t int) int { } func chfind(t int, s string) int { - if s[0] == ' ' { + if s[0] == '"' || s[0] == '\'' { t = 0 } for i := 0; i <= ntokens; i++ { @@ -1516,9 +1469,6 @@ func symnam(i int) string { } else { s = tokset[i].name } - if s[0] == ' ' { - s = s[1:] - } return s } -- cgit v1.3 From 754cd5419ace8b1cdc615c3fe58febbcec7b61a0 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Fri, 26 Sep 2014 17:09:11 -0400 Subject: cmd/go: always build _test.go files and link into test go test's handling of _test.go files when the entire package's set of files has no Test functions has varied over the past few releases. There are a few interesting cases (all contain no Test functions): (1) x_test.go has syntax errors (2) x_test.go has type errors (3) x_test.go has runtime errors (say, a func init that panics) In Go 1.1, tests with (1) or (2) failed; (3) passed. In Go 1.2, tests with (1) or (2) failed; (3) passed. In Go 1.3, tests with (1) failed; (2) or (3) passed. After this CL, tests with (1), (2), or (3) all fail. This is clearly a corner case, but it seems to me that the behavior of the test should not change if you add or remove a line like func TestAlwaysPasses(t *testing.T) {} That implies that the _test.go files must always be built and always be imported into the test binary. Doing so means that (1), (2), and (3) must all fail. Fixes #8337. LGTM=iant R=golang-codereviews, iant CC=adg, golang-codereviews, r https://golang.org/cl/150980043 --- src/cmd/go/test.bash | 14 ++++++++++++++ src/cmd/go/test.go | 6 ++++-- src/cmd/go/testdata/src/badtest/badexec/x_test.go | 5 +++++ src/cmd/go/testdata/src/badtest/badsyntax/x.go | 1 + src/cmd/go/testdata/src/badtest/badsyntax/x_test.go | 3 +++ src/cmd/go/testdata/src/badtest/badvar/x.go | 1 + src/cmd/go/testdata/src/badtest/badvar/x_test.go | 5 +++++ 7 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/cmd/go/testdata/src/badtest/badexec/x_test.go create mode 100644 src/cmd/go/testdata/src/badtest/badsyntax/x.go create mode 100644 src/cmd/go/testdata/src/badtest/badsyntax/x_test.go create mode 100644 src/cmd/go/testdata/src/badtest/badvar/x.go create mode 100644 src/cmd/go/testdata/src/badtest/badvar/x_test.go (limited to 'src/cmd') diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index 1284876193..6a72bcde07 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -71,6 +71,20 @@ if ! grep -q "/tool/.*/$linker" $d/err.out; then fi rm -r $d +TEST broken tests without Test functions all fail +d=$(mktemp -d -t testgoXXX) +./testgo test ./testdata/src/badtest/... >$d/err 2>&1 || true +if grep -q '^ok' $d/err; then + echo test passed unexpectedly: + grep '^ok' $d/err + ok=false +elif ! grep -q 'FAIL.*badtest/badexec' $d/err || ! grep -q 'FAIL.*badtest/badsyntax' $d/err || ! grep -q 'FAIL.*badtest/badvar' $d/err; then + echo test did not run everything + cat $d/err + ok=false +fi +rm -rf $d + TEST 'go build -a in dev branch' ./testgo install math || ok=false # should be up to date already but just in case d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 100ef5fa82..0962e5bb50 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -736,11 +736,13 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, if err != nil { return nil, nil, nil, err } - if t.ImportTest || ptest.coverMode != "" { + if len(ptest.GoFiles) > 0 { pmain.imports = append(pmain.imports, ptest) + t.ImportTest = true } - if t.ImportXtest { + if pxtest != nil { pmain.imports = append(pmain.imports, pxtest) + t.ImportXtest = true } if ptest != p && localCover { diff --git a/src/cmd/go/testdata/src/badtest/badexec/x_test.go b/src/cmd/go/testdata/src/badtest/badexec/x_test.go new file mode 100644 index 0000000000..12f5051712 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badexec/x_test.go @@ -0,0 +1,5 @@ +package badexec + +func init() { + panic("badexec") +} diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x.go b/src/cmd/go/testdata/src/badtest/badsyntax/x.go new file mode 100644 index 0000000000..c8a5407a5a --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badsyntax/x.go @@ -0,0 +1 @@ +package badsyntax diff --git a/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go new file mode 100644 index 0000000000..5be10745d9 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badsyntax/x_test.go @@ -0,0 +1,3 @@ +package badsyntax + +func func func func func! diff --git a/src/cmd/go/testdata/src/badtest/badvar/x.go b/src/cmd/go/testdata/src/badtest/badvar/x.go new file mode 100644 index 0000000000..fdd46c4c72 --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badvar/x.go @@ -0,0 +1 @@ +package badvar diff --git a/src/cmd/go/testdata/src/badtest/badvar/x_test.go b/src/cmd/go/testdata/src/badtest/badvar/x_test.go new file mode 100644 index 0000000000..c67df01c5c --- /dev/null +++ b/src/cmd/go/testdata/src/badtest/badvar/x_test.go @@ -0,0 +1,5 @@ +package badvar_test + +func f() { + _ = notdefined +} -- cgit v1.3 From f6fc14094a476d2e23722f124cfcd8204c2659b0 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 28 Sep 2014 08:27:05 -0700 Subject: cmd/ld: don't automatically mark symbols created by -X as reachable This fixes the bug in which the linker reports "missing Go type information" when a -X option refers to a symbol that is not used. Fixes #8821. LGTM=rsc R=rsc, r CC=golang-codereviews https://golang.org/cl/151000043 --- src/cmd/ld/data.c | 9 +++++++-- src/cmd/ld/lib.c | 4 +++- test/linkx.go | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 89226bfe28..9983a9281c 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -620,6 +620,7 @@ addstrdata(char *name, char *value) { LSym *s, *sp; char *p; + uchar reachable; p = smprint("%s.str", name); sp = linklookup(ctxt, p, 0); @@ -630,13 +631,17 @@ addstrdata(char *name, char *value) s = linklookup(ctxt, name, 0); s->size = 0; s->dupok = 1; + reachable = s->reachable; addaddr(ctxt, s, sp); adduint32(ctxt, s, strlen(value)); if(PtrSize == 8) adduint32(ctxt, s, 0); // round struct to pointer width - // in case reachability has already been computed - sp->reachable = s->reachable; + // addstring, addaddr, etc., mark the symbols as reachable. + // In this case that is not necessarily true, so stick to what + // we know before entering this function. + s->reachable = reachable; + sp->reachable = reachable; } vlong diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 3edf7253d4..f889aba8a9 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -222,8 +222,10 @@ loadlib(void) // Since we are simulating the import, we have to provide this string. cgostrsym = "go.string.\"runtime/cgo\""; if(linkrlookup(ctxt, cgostrsym, 0) == nil) { + s = linklookup(ctxt, cgostrsym, 0); + s->type = SRODATA; + s->reachable = 1; addstrdata(cgostrsym, "runtime/cgo"); - linklookup(ctxt, cgostrsym, 0)->type = SRODATA; } } diff --git a/test/linkx.go b/test/linkx.go index 36d16aec9b..06888a229a 100644 --- a/test/linkx.go +++ b/test/linkx.go @@ -1,4 +1,4 @@ -// $G $D/$F.go && $L -X main.tbd hello -X main.overwrite trumped $F.$A && ./$A.out +// $G $D/$F.go && $L -X main.tbd hello -X main.overwrite trumped -X main.nosuchsymbol neverseen $F.$A && ./$A.out // NOTE: This test is not run by 'run.go' and so not run by all.bash. // To run this test you must use the ./run shell script. -- cgit v1.3 From e7e3b3ec1037669c90851670e2dc608b80d499d6 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Mon, 29 Sep 2014 12:13:22 -0700 Subject: cmd/ld: close outfile before cleanup This prevents the temporary directory from being leaked when the linker is run on a FUSE filesystem. Fixes #8684. LGTM=bradfitz R=golang-codereviews, rsc, bradfitz CC=golang-codereviews https://golang.org/cl/141840043 --- src/cmd/ld/lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index f889aba8a9..910201bdbb 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -531,8 +531,9 @@ char* mktempdir(void); void removeall(char*); static void -rmtemp(void) +cleanup(void) { + close(cout); removeall(tmpdir); } @@ -547,7 +548,7 @@ hostlinksetup(void) // create temporary directory and arrange cleanup if(tmpdir == nil) { tmpdir = mktempdir(); - atexit(rmtemp); + atexit(cleanup); } // change our output to temporary object file -- cgit v1.3 From 1cfa5958f0ab841bd00fcc0d674bfec87e2055bd Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 29 Sep 2014 13:28:08 -0700 Subject: undo CL 141840043 / 65e21380cb2a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unnecessary; covered by https://golang.org/cl/141690043 Verified by jonathan@titanous.com on golang-dev. ««« original CL description cmd/ld: close outfile before cleanup This prevents the temporary directory from being leaked when the linker is run on a FUSE filesystem. Fixes #8684. LGTM=bradfitz R=golang-codereviews, rsc, bradfitz CC=golang-codereviews https://golang.org/cl/141840043 »»» LGTM=jonathan, iant R=iant, jonathan CC=golang-codereviews https://golang.org/cl/150250045 --- src/cmd/ld/lib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 910201bdbb..f889aba8a9 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -531,9 +531,8 @@ char* mktempdir(void); void removeall(char*); static void -cleanup(void) +rmtemp(void) { - close(cout); removeall(tmpdir); } @@ -548,7 +547,7 @@ hostlinksetup(void) // create temporary directory and arrange cleanup if(tmpdir == nil) { tmpdir = mktempdir(); - atexit(cleanup); + atexit(rmtemp); } // change our output to temporary object file -- cgit v1.3 From fe2bc11e1fac672cc23e9ffd01673257a1330707 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 29 Sep 2014 13:32:14 -0700 Subject: cmd/yacc: fix handling of tokens that don't start with letters CL 149110043 changed yacc to no longer keep a leading space for quoted tokens. That is OK by itself but unfortunately yacc was relying on that leading space to notice which tokens it should not output as const declarations. Add a few such tokens to expr.y, although it won't make any immediate difference as we seem to have no tests for yacc. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/152720043 --- src/cmd/yacc/testdata/expr/expr.y | 2 ++ src/cmd/yacc/yacc.go | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'src/cmd') diff --git a/src/cmd/yacc/testdata/expr/expr.y b/src/cmd/yacc/testdata/expr/expr.y index 09451949ff..721b1c9172 100644 --- a/src/cmd/yacc/testdata/expr/expr.y +++ b/src/cmd/yacc/testdata/expr/expr.y @@ -32,6 +32,8 @@ import ( %type expr expr1 expr2 expr3 +%token '+' '-' '*' '/' '(' ')' + %token NUM %% diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index 0761811cf4..4dba376fc2 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -195,8 +195,9 @@ type Item struct { } type Symb struct { - name string - value int + name string + noconst bool + value int } type Wset struct { @@ -509,8 +510,7 @@ outer: // put out non-literal terminals for i := TOKSTART; i <= ntokens; i++ { // non-literals - c := tokset[i].name[0] - if c != ' ' && c != '$' { + if !tokset[i].noconst { fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value) } } @@ -734,7 +734,7 @@ func defin(nt int, s string) int { copy(anontrst, nontrst) nontrst = anontrst } - nontrst[nnonter] = Symb{s, 0} + nontrst[nnonter] = Symb{name: s} return NTBASE + nnonter } @@ -769,9 +769,13 @@ func defin(nt int, s string) int { if val == 0 { errorf("token value 0 is illegal") } + tokset[ntokens].noconst = true } else { val = extval extval++ + if s[0] == '$' { + tokset[ntokens].noconst = true + } } tokset[ntokens].value = val -- cgit v1.3 From 912ec1990bd09f8fc128c3fa6b59105085aabc03 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Mon, 29 Sep 2014 17:04:48 -0700 Subject: go/format, cmd/gofmt: fix issues with partial Go code with indent Fixes #5551. Fixes #4449. Adds tests for both issues. Note that the two issues occur only when formatting partial Go code with indent. The best way to understand the change is as follows: I took the code of cmd/gofmt and go/format, combined it into one unified code that does not suffer from either 4449 nor 5551, and then applied that code to both cmd/gofmt and go/format. As a result, there is now much more identical code between the two packages, making future code deduplication easier (it was not possible to do that now without adding public APIs, which I was advised not to do at this time). More specifically, I took the parse() of cmd/gofmt which correctly preserves comments (issue 5551) and modified it to fix issue where it would sometimes modify literal values (issue 4449). I ended up removing the matchSpace() function because it no longer needed to do some of its work (insert indent), and a part of its work had to be done in advance (determining the indentation of first code line), because that calculation is required for cfg.Fprint() to run. adjustIndent is used to adjust the indent of cfg.Fprint() to compensate for the body of wrapper func being indented by one level. This allows to get rid of the bytes.Replace text manipulation of inner content, which was problematic and sometimes altered raw string literals (issue 4449). This means that sometimes the value of cfg.Indent is negative, but that works as expected. So now the algorithm for formatting partial Go code is: 1. Determine and prepend leading space of original source. 2. Determine and prepend indentation of first code line. 3. Format and write partial Go code (with all of its leading & trailing space trimmed). 4. Determine and append trailing space of original source. LGTM=gri R=golang-codereviews, bradfitz, gri CC=golang-codereviews https://golang.org/cl/142360043 --- src/cmd/gofmt/gofmt.go | 163 +++++++++++++++------------ src/cmd/gofmt/long_test.go | 4 +- src/cmd/gofmt/testdata/stdin6.golden | 19 ++++ src/cmd/gofmt/testdata/stdin6.input | 21 ++++ src/cmd/gofmt/testdata/stdin7.golden | 19 ++++ src/cmd/gofmt/testdata/stdin7.input | 21 ++++ src/go/format/format.go | 207 +++++++++++++++++++++-------------- src/go/format/format_test.go | 6 +- 8 files changed, 305 insertions(+), 155 deletions(-) create mode 100644 src/cmd/gofmt/testdata/stdin6.golden create mode 100644 src/cmd/gofmt/testdata/stdin6.input create mode 100644 src/cmd/gofmt/testdata/stdin7.golden create mode 100644 src/cmd/gofmt/testdata/stdin7.input (limited to 'src/cmd') diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index f322a2b0a0..8542957248 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -87,13 +87,13 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error return err } - file, adjust, err := parse(fileSet, filename, src, stdin) + file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin) if err != nil { return err } if rewrite != nil { - if adjust == nil { + if sourceAdj == nil { file = rewrite(file) } else { fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n") @@ -106,15 +106,10 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error simplify(file) } - var buf bytes.Buffer - err = (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fileSet, file) + res, err := format(fileSet, file, sourceAdj, indentAdj, src) if err != nil { return err } - res := buf.Bytes() - if adjust != nil { - res = adjust(src, res) - } if !bytes.Equal(src, res) { // formatting has changed @@ -242,17 +237,19 @@ func diff(b1, b2 []byte) (data []byte, err error) { // parse parses src, which was read from filename, // as a Go source file or statement list. -func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.File, func(orig, src []byte) []byte, error) { +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { // Try as whole source file. - file, err := parser.ParseFile(fset, filename, src, parserMode) - if err == nil { - return file, nil, nil - } - // If the error is that the source file didn't begin with a - // package line and this is standard input, fall through to + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to // try as a source fragment. Stop and return on any other error. - if !stdin || !strings.Contains(err.Error(), "expected 'package'") { - return nil, nil, err + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return } // If this is a declaration list, make it a source file @@ -262,19 +259,19 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F psrc := append([]byte("package p;"), src...) file, err = parser.ParseFile(fset, filename, psrc, parserMode) if err == nil { - adjust := func(orig, src []byte) []byte { + sourceAdj = func(src []byte, indent int) []byte { // Remove the package clause. // Gofmt has turned the ; into a \n. - src = src[len("package p\n"):] - return matchSpace(orig, src) + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) } - return file, adjust, nil + return } // If the error is that the source file didn't begin with a // declaration, fall through to try as a statement list. // Stop and return on any other error. if !strings.Contains(err.Error(), "expected declaration") { - return nil, nil, err + return } // If this is a statement list, make it a source file @@ -285,65 +282,89 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}') file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { - adjust := func(orig, src []byte) []byte { + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } // Remove the wrapping. // Gofmt has turned the ; into a \n\n. - src = src[len("package p\n\nfunc _() {"):] - src = src[:len(src)-len("\n}\n")] - // Gofmt has also indented the function body one level. - // Remove that indent. - src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) - return matchSpace(orig, src) + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + src = src[:len(src)-(indent+len("\n}\n"))] + return bytes.TrimSpace(src) } - return file, adjust, nil + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 } - // Failed, and out of options. - return nil, nil, err + // Succeeded, or out of options. + return } -func cutSpace(b []byte) (before, middle, after []byte) { - i := 0 - for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') { - i++ - } - j := len(b) - for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') { - j-- - } - if i <= j { - return b[:i], b[i:j], b[j:] +func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + var buf bytes.Buffer + err := (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil } - return nil, nil, b[j:] -} -// matchSpace reformats src to use the same space context as orig. -// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src. -// 2) matchSpace copies the indentation of the first non-blank line in orig -// to every non-blank line in src. -// 3) matchSpace copies the trailing space from orig and uses it in place -// of src's trailing space. -func matchSpace(orig []byte, src []byte) []byte { - before, _, after := cutSpace(orig) - i := bytes.LastIndex(before, []byte{'\n'}) - before, indent := before[:i+1], before[i+1:] - - _, src, _ = cutSpace(src) - - var b bytes.Buffer - b.Write(before) - for len(src) > 0 { - line := src - if i := bytes.IndexByte(line, '\n'); i >= 0 { - line, src = line[:i+1], line[i+1:] - } else { - src = nil + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space } - if len(line) > 0 && line[0] != '\n' { // not blank - b.Write(indent) + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ } - b.Write(line) } - b.Write(after) - return b.Bytes() + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg := &printer.Config{Mode: printerMode, Tabwidth: tabWidth} + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...) + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + return append(res, src[i:]...), nil +} + +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index 108278b336..237b86021b 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -32,7 +32,7 @@ var ( ) func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { - f, _, err := parse(fset, filename, src.Bytes(), false) + f, _, _, err := parse(fset, filename, src.Bytes(), false) if err != nil { return err } @@ -60,7 +60,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { // exclude files w/ syntax errors (typically test cases) fset := token.NewFileSet() - if _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { + if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { if *verbose { fmt.Fprintf(os.Stderr, "ignoring %s\n", err) } diff --git a/src/cmd/gofmt/testdata/stdin6.golden b/src/cmd/gofmt/testdata/stdin6.golden new file mode 100644 index 0000000000..ffcea8011b --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` +foo + + +`+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin6.input b/src/cmd/gofmt/testdata/stdin6.input new file mode 100644 index 0000000000..78330020c6 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` +foo + + +`+ tail , + "more" , + "and more" ) + } diff --git a/src/cmd/gofmt/testdata/stdin7.golden b/src/cmd/gofmt/testdata/stdin7.golden new file mode 100644 index 0000000000..bbac7133c8 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` + foo + + + `+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin7.input b/src/cmd/gofmt/testdata/stdin7.input new file mode 100644 index 0000000000..fd772a3c4e --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` + foo + + + `+ tail , + "more" , + "and more" ) + } diff --git a/src/go/format/format.go b/src/go/format/format.go index 3d00a645db..08a9047b99 100644 --- a/src/go/format/format.go +++ b/src/go/format/format.go @@ -18,6 +18,8 @@ import ( var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} +const parserMode = parser.ParseComments + // Node formats node in canonical gofmt style and writes the result to dst. // // The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, @@ -52,7 +54,7 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { if err != nil { return err } - file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments) + file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode) if err != nil { // We should never get here. If we do, provide good diagnostic. return fmt.Errorf("format.Node internal error (%s)", err) @@ -80,66 +82,12 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { // func Source(src []byte) ([]byte, error) { fset := token.NewFileSet() - node, err := parse(fset, src) + file, sourceAdj, indentAdj, err := parse(fset, "", src, true) if err != nil { return nil, err } - var buf bytes.Buffer - if file, ok := node.(*ast.File); ok { - // Complete source file. - ast.SortImports(fset, file) - err := config.Fprint(&buf, fset, file) - if err != nil { - return nil, err - } - - } else { - // Partial source file. - // Determine and prepend leading space. - i, j := 0, 0 - for j < len(src) && isSpace(src[j]) { - if src[j] == '\n' { - i = j + 1 // index of last line in leading space - } - j++ - } - buf.Write(src[:i]) - - // Determine indentation of first code line. - // Spaces are ignored unless there are no tabs, - // in which case spaces count as one tab. - indent := 0 - hasSpace := false - for _, b := range src[i:j] { - switch b { - case ' ': - hasSpace = true - case '\t': - indent++ - } - } - if indent == 0 && hasSpace { - indent = 1 - } - - // Format the source. - cfg := config - cfg.Indent = indent - err := cfg.Fprint(&buf, fset, node) - if err != nil { - return nil, err - } - - // Determine and append trailing space. - i = len(src) - for i > 0 && isSpace(src[i-1]) { - i-- - } - buf.Write(src[i:]) - } - - return buf.Bytes(), nil + return format(fset, file, sourceAdj, indentAdj, src) } func hasUnsortedImports(file *ast.File) bool { @@ -160,40 +108,137 @@ func hasUnsortedImports(file *ast.File) bool { return false } -func isSpace(b byte) bool { - return b == ' ' || b == '\t' || b == '\n' || b == '\r' -} +// parse parses src, which was read from filename, +// as a Go source file or statement list. +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { + // Try as whole source file. + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to + // try as a source fragment. Stop and return on any other error. + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return + } -func parse(fset *token.FileSet, src []byte) (interface{}, error) { - // Try as a complete source file. - file, err := parser.ParseFile(fset, "", src, parser.ParseComments) + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ;, not a newline, so that the line numbers + // in psrc match the ones in src. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) if err == nil { - return file, nil + sourceAdj = func(src []byte, indent int) []byte { + // Remove the package clause. + // Gofmt has turned the ; into a \n. + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) + } + return } - // If the source is missing a package clause, try as a source fragment; otherwise fail. - if !strings.Contains(err.Error(), "expected 'package'") { - return nil, err + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return } - // Try as a declaration list by prepending a package clause in front of src. - // Use ';' not '\n' to keep line numbers intact. - psrc := append([]byte("package p;"), src...) - file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments) + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ;, not a newline, so that the line numbers + // in fsrc match the ones in src. + fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { - return file.Decls, nil + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } + // Remove the wrapping. + // Gofmt has turned the ; into a \n\n. + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + src = src[:len(src)-(indent+len("\n}\n"))] + return bytes.TrimSpace(src) + } + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 } - // If the source is missing a declaration, try as a statement list; otherwise fail. - if !strings.Contains(err.Error(), "expected declaration") { + + // Succeeded, or out of options. + return +} + +func format(fset *token.FileSet, file *ast.File, sourceAdj func(src []byte, indent int) []byte, indentAdj int, src []byte) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + ast.SortImports(fset, file) + var buf bytes.Buffer + err := config.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil + } + + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space + } + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg := config + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { return nil, err } + res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...) - // Try as statement list by wrapping a function around src. - fsrc := append(append([]byte("package p; func _() {"), src...), '}') - file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments) - if err == nil { - return file.Decls[0].(*ast.FuncDecl).Body.List, nil + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- } + return append(res, src[i:]...), nil +} - // Failed, and out of options. - return nil, err +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go index 93f0992477..d7846bec65 100644 --- a/src/go/format/format_test.go +++ b/src/go/format/format_test.go @@ -87,7 +87,11 @@ var tests = []string{ "\tx := 0\n\tgo f()\n\n\n", "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n", "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n", - "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation added inside raw strings + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\n\t\tfoo\n`\n\n\n", // no indentation removed inside raw strings + + // comments + "i := 5 /* Comment */", // Issue 5551. // erroneous programs "ERROR1 + 2 +", -- cgit v1.3 From c75f81f0edd73d5f7d6528cec795b49c5d205c0c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 30 Sep 2014 12:28:24 -0400 Subject: cmd/objdump: move armasm, x86asm into internal packages For Go 1.3 these external packages were collapsed into large single-file implementations stored in the cmd/objdump directory. For Go 1.4 we want pprof to be able to link against them too, so move them into cmd/internal, where they can be shared. The new files are copied from the repo in the file path (rsc.io/...). Those repos were code reviewed during development (mainly by crawshaw and minux), because we knew the main repo would use them. Update #8798 LGTM=bradfitz R=crawshaw, bradfitz CC=golang-codereviews https://golang.org/cl/153750044 --- src/cmd/internal/rsc.io/arm/armasm/Makefile | 2 + src/cmd/internal/rsc.io/arm/armasm/decode.go | 567 + src/cmd/internal/rsc.io/arm/armasm/decode_test.go | 69 + src/cmd/internal/rsc.io/arm/armasm/ext_test.go | 614 + src/cmd/internal/rsc.io/arm/armasm/gnu.go | 164 + src/cmd/internal/rsc.io/arm/armasm/inst.go | 438 + src/cmd/internal/rsc.io/arm/armasm/objdump_test.go | 258 + .../internal/rsc.io/arm/armasm/objdumpext_test.go | 260 + src/cmd/internal/rsc.io/arm/armasm/plan9x.go | 211 + src/cmd/internal/rsc.io/arm/armasm/tables.go | 9448 +++++++++++++ .../internal/rsc.io/arm/armasm/testdata/Makefile | 5 + .../internal/rsc.io/arm/armasm/testdata/decode.txt | 306 + src/cmd/internal/rsc.io/x86/x86asm/Makefile | 3 + src/cmd/internal/rsc.io/x86/x86asm/decode.go | 1616 +++ src/cmd/internal/rsc.io/x86/x86asm/decode_test.go | 71 + src/cmd/internal/rsc.io/x86/x86asm/ext_test.go | 811 ++ src/cmd/internal/rsc.io/x86/x86asm/gnu.go | 926 ++ src/cmd/internal/rsc.io/x86/x86asm/inst.go | 641 + src/cmd/internal/rsc.io/x86/x86asm/inst_test.go | 20 + src/cmd/internal/rsc.io/x86/x86asm/intel.go | 518 + src/cmd/internal/rsc.io/x86/x86asm/objdump_test.go | 383 + .../internal/rsc.io/x86/x86asm/objdumpext_test.go | 314 + .../internal/rsc.io/x86/x86asm/plan9ext_test.go | 120 + src/cmd/internal/rsc.io/x86/x86asm/plan9x.go | 346 + src/cmd/internal/rsc.io/x86/x86asm/plan9x_test.go | 54 + src/cmd/internal/rsc.io/x86/x86asm/tables.go | 9760 +++++++++++++ .../internal/rsc.io/x86/x86asm/testdata/Makefile | 12 + .../internal/rsc.io/x86/x86asm/testdata/decode.txt | 6731 +++++++++ src/cmd/internal/rsc.io/x86/x86asm/xed_test.go | 211 + src/cmd/internal/rsc.io/x86/x86asm/xedext_test.go | 206 + src/cmd/objdump/armasm.go | 10821 --------------- src/cmd/objdump/main.go | 11 +- src/cmd/objdump/x86.go | 13800 ------------------- 33 files changed, 35092 insertions(+), 24625 deletions(-) create mode 100644 src/cmd/internal/rsc.io/arm/armasm/Makefile create mode 100644 src/cmd/internal/rsc.io/arm/armasm/decode.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/decode_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/ext_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/gnu.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/inst.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/objdump_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/plan9x.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/tables.go create mode 100644 src/cmd/internal/rsc.io/arm/armasm/testdata/Makefile create mode 100644 src/cmd/internal/rsc.io/arm/armasm/testdata/decode.txt create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/Makefile create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/decode.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/decode_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/ext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/gnu.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/inst.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/inst_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/intel.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/objdump_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/objdumpext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9ext_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9x.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/plan9x_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/tables.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/testdata/Makefile create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/testdata/decode.txt create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/xed_test.go create mode 100644 src/cmd/internal/rsc.io/x86/x86asm/xedext_test.go delete mode 100644 src/cmd/objdump/armasm.go delete mode 100644 src/cmd/objdump/x86.go (limited to 'src/cmd') diff --git a/src/cmd/internal/rsc.io/arm/armasm/Makefile b/src/cmd/internal/rsc.io/arm/armasm/Makefile new file mode 100644 index 0000000000..a3f57001f6 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/Makefile @@ -0,0 +1,2 @@ +tables.go: ../armmap/map.go ../arm.csv + go run ../armmap/map.go -fmt=decoder ../arm.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go diff --git a/src/cmd/internal/rsc.io/arm/armasm/decode.go b/src/cmd/internal/rsc.io/arm/armasm/decode.go new file mode 100644 index 0000000000..6b4d73841b --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/decode.go @@ -0,0 +1,567 @@ +// Copyright 2014 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 armasm + +import ( + "encoding/binary" + "fmt" +) + +// An instFormat describes the format of an instruction encoding. +// An instruction with 32-bit value x matches the format if x&mask == value +// and the condition matches. +// The condition matches if x>>28 == 0xF && value>>28==0xF +// or if x>>28 != 0xF and value>>28 == 0. +// If x matches the format, then the rest of the fields describe how to interpret x. +// The opBits describe bits that should be extracted from x and added to the opcode. +// For example opBits = 0x1234 means that the value +// (2 bits at offset 1) followed by (4 bits at offset 3) +// should be added to op. +// Finally the args describe how to decode the instruction arguments. +// args is stored as a fixed-size array; if there are fewer than len(args) arguments, +// args[i] == 0 marks the end of the argument list. +type instFormat struct { + mask uint32 + value uint32 + priority int8 + op Op + opBits uint64 + args instArgs +} + +type instArgs [4]instArg + +var ( + errMode = fmt.Errorf("unsupported execution mode") + errShort = fmt.Errorf("truncated instruction") + errUnknown = fmt.Errorf("unknown instruction") +) + +var decoderCover []bool + +// Decode decodes the leading bytes in src as a single instruction. +func Decode(src []byte, mode Mode) (inst Inst, err error) { + if mode != ModeARM { + return Inst{}, errMode + } + if len(src) < 4 { + return Inst{}, errShort + } + + if decoderCover == nil { + decoderCover = make([]bool, len(instFormats)) + } + + x := binary.LittleEndian.Uint32(src) + + // The instFormat table contains both conditional and unconditional instructions. + // Considering only the top 4 bits, the conditional instructions use mask=0, value=0, + // while the unconditional instructions use mask=f, value=f. + // Prepare a version of x with the condition cleared to 0 in conditional instructions + // and then assume mask=f during matching. + const condMask = 0xf0000000 + xNoCond := x + if x&condMask != condMask { + xNoCond &^= condMask + } + var priority int8 +Search: + for i := range instFormats { + f := &instFormats[i] + if xNoCond&(f.mask|condMask) != f.value || f.priority <= priority { + continue + } + delta := uint32(0) + deltaShift := uint(0) + for opBits := f.opBits; opBits != 0; opBits >>= 16 { + n := uint(opBits & 0xFF) + off := uint((opBits >> 8) & 0xFF) + delta |= (x >> off) & (1<> 8) & (1<<4 - 1)) + case arg_R_12: + return Reg((x >> 12) & (1<<4 - 1)) + case arg_R_16: + return Reg((x >> 16) & (1<<4 - 1)) + + case arg_R_12_nzcv: + r := Reg((x >> 12) & (1<<4 - 1)) + if r == R15 { + return APSR_nzcv + } + return r + + case arg_R_16_WB: + mode := AddrLDM + if (x>>21)&1 != 0 { + mode = AddrLDM_WB + } + return Mem{Base: Reg((x >> 16) & (1<<4 - 1)), Mode: mode} + + case arg_R_rotate: + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + // ROR #0 here means ROR #0, but decodeShift rewrites to RRX #1. + if typ == RotateRightExt { + return Reg(Rm) + } + return RegShift{Rm, typ, uint8(count)} + + case arg_R_shift_R: + Rm := Reg(x & (1<<4 - 1)) + Rs := Reg((x >> 8) & (1<<4 - 1)) + typ := Shift((x >> 5) & (1<<2 - 1)) + return RegShiftReg{Rm, typ, Rs} + + case arg_R_shift_imm: + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + if typ == ShiftLeft && count == 0 { + return Reg(Rm) + } + return RegShift{Rm, typ, uint8(count)} + + case arg_R1_0: + return Reg((x & (1<<4 - 1))) + case arg_R1_12: + return Reg(((x >> 12) & (1<<4 - 1))) + case arg_R2_0: + return Reg((x & (1<<4 - 1)) | 1) + case arg_R2_12: + return Reg(((x >> 12) & (1<<4 - 1)) | 1) + + case arg_SP: + return SP + + case arg_Sd_Dd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Dd_Sd: + return decodeArg(arg_Sd_Dd, x^(1<<8)) + + case arg_Sd: + v := (x >> 12) & (1<<4 - 1) + vx := (x >> 22) & 1 + return S0 + Reg(v<<1+vx) + + case arg_Sm_Dm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Sm: + v := (x >> 0) & (1<<4 - 1) + vx := (x >> 5) & 1 + return S0 + Reg(v<<1+vx) + + case arg_Dn_half: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return RegX{D0 + Reg(vx<<4+v), int((x >> 21) & 1)} + + case arg_Sn_Dn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + sz := (x >> 8) & 1 + if sz != 0 { + return D0 + Reg(vx<<4+v) + } else { + return S0 + Reg(v<<1+vx) + } + + case arg_Sn: + v := (x >> 16) & (1<<4 - 1) + vx := (x >> 7) & 1 + return S0 + Reg(v<<1+vx) + + case arg_const: + v := x & (1<<8 - 1) + rot := (x >> 8) & (1<<4 - 1) * 2 + if rot > 0 && v&3 == 0 { + // could rotate less + return ImmAlt{uint8(v), uint8(rot)} + } + if rot >= 24 && ((v<<(32-rot))&0xFF)>>(32-rot) == v { + // could wrap around to rot==0. + return ImmAlt{uint8(v), uint8(rot)} + } + return Imm(v>>rot | v<<(32-rot)) + + case arg_endian: + return Endian((x >> 9) & 1) + + case arg_fbits: + return Imm((16 << ((x >> 7) & 1)) - ((x&(1<<4-1))<<1 | (x>>5)&1)) + + case arg_fp_0: + return Imm(0) + + case arg_imm24: + return Imm(x & (1<<24 - 1)) + + case arg_imm5: + return Imm((x >> 7) & (1<<5 - 1)) + + case arg_imm5_32: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + x = 32 + } + return Imm(x) + + case arg_imm5_nz: + x = (x >> 7) & (1<<5 - 1) + if x == 0 { + return nil + } + return Imm(x) + + case arg_imm_4at16_12at0: + return Imm((x>>16)&(1<<4-1)<<12 | x&(1<<12-1)) + + case arg_imm_12at8_4at0: + return Imm((x>>8)&(1<<12-1)<<4 | x&(1<<4-1)) + + case arg_imm_vfp: + x = (x>>16)&(1<<4-1)<<4 | x&(1<<4-1) + return Imm(x) + + case arg_label24: + imm := (x & (1<<24 - 1)) << 2 + return PCRel(int32(imm<<6) >> 6) + + case arg_label24H: + h := (x >> 24) & 1 + imm := (x&(1<<24-1))<<2 | h<<1 + return PCRel(int32(imm<<6) >> 6) + + case arg_label_m_12: + d := int32(x & (1<<12 - 1)) + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(-d)} + + case arg_label_p_12: + d := int32(x & (1<<12 - 1)) + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} + + case arg_label_pm_12: + d := int32(x & (1<<12 - 1)) + u := (x >> 23) & 1 + if u == 0 { + d = -d + } + return Mem{Base: PC, Mode: AddrOffset, Offset: int16(d)} + + case arg_label_pm_4_4: + d := int32((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) + u := (x >> 23) & 1 + if u == 0 { + d = -d + } + return PCRel(d) + + case arg_lsb_width: + lsb := (x >> 7) & (1<<5 - 1) + msb := (x >> 16) & (1<<5 - 1) + if msb < lsb || msb >= 32 { + return nil + } + return Imm(msb + 1 - lsb) + + case arg_mem_R: + Rn := Reg((x >> 16) & (1<<4 - 1)) + return Mem{Base: Rn, Mode: AddrOffset} + + case arg_mem_R_pm_R_postindex: + // Treat [],+/- like [,+/-{,}]{!} + // by forcing shift bits to <<0 and P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5|1<<24|1<<21)) + + case arg_mem_R_pm_R_W: + // Treat [,+/-]{!} like [,+/-{,}]{!} + // by forcing shift bits to <<0. + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^((1<<7-1)<<5)) + + case arg_mem_R_pm_R_shift_imm_offset: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<21)|1<<24) + + case arg_mem_R_pm_R_shift_imm_postindex: + // Treat [],+/-{,} like [,+/-{,}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_R_shift_imm_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_R_shift_imm_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + Rm := Reg(x & (1<<4 - 1)) + typ, count := decodeShift(x) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Sign: sign, Index: Rm, Shift: typ, Count: count} + + case arg_mem_R_pm_imm12_offset: + // Treat [,#+/-] like [{,#+/-}]{!} + // by forcing P=1, W=0 (index=false, wback=false). + return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<21)|1<<24) + + case arg_mem_R_pm_imm12_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_imm12_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_imm12_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16(x & (1<<12 - 1)) + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arg_mem_R_pm_imm8_postindex: + // Treat [],#+/- like [{,#+/-}]{!} + // by forcing P=0, W=0 (postindex=true). + return decodeArg(arg_mem_R_pm_imm8_W, x&^(1<<24|1<<21)) + + case arg_mem_R_pm_imm8_W: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + w := (x >> 21) & 1 + p := (x >> 24) & 1 + if p == 0 && w == 1 { + return nil + } + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16((x>>8)&(1<<4-1)<<4 | x&(1<<4-1)) + mode := AddrMode(uint8(p<<1) | uint8(w^1)) + return Mem{Base: Rn, Mode: mode, Offset: int16(sign) * imm} + + case arg_mem_R_pm_imm8at0_offset: + Rn := Reg((x >> 16) & (1<<4 - 1)) + u := (x >> 23) & 1 + sign := int8(+1) + if u == 0 { + sign = -1 + } + imm := int16(x&(1<<8-1)) << 2 + return Mem{Base: Rn, Mode: AddrOffset, Offset: int16(sign) * imm} + + case arg_option: + return Imm(x & (1<<4 - 1)) + + case arg_registers: + return RegList(x & (1<<16 - 1)) + + case arg_registers2: + x &= 1<<16 - 1 + n := 0 + for i := 0; i < 16; i++ { + if x>>uint(i)&1 != 0 { + n++ + } + } + if n < 2 { + return nil + } + return RegList(x) + + case arg_registers1: + Rt := (x >> 12) & (1<<4 - 1) + return RegList(1 << Rt) + + case arg_satimm4: + return Imm((x >> 16) & (1<<4 - 1)) + + case arg_satimm5: + return Imm((x >> 16) & (1<<5 - 1)) + + case arg_satimm4m1: + return Imm((x>>16)&(1<<4-1) + 1) + + case arg_satimm5m1: + return Imm((x>>16)&(1<<5-1) + 1) + + case arg_widthm1: + return Imm((x>>16)&(1<<5-1) + 1) + + } +} + +// decodeShift decodes the shift-by-immediate encoded in x. +func decodeShift(x uint32) (Shift, uint8) { + count := (x >> 7) & (1<<5 - 1) + typ := Shift((x >> 5) & (1<<2 - 1)) + switch typ { + case ShiftRight, ShiftRightSigned: + if count == 0 { + count = 32 + } + case RotateRight: + if count == 0 { + typ = RotateRightExt + count = 1 + } + } + return typ, uint8(count) +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/decode_test.go b/src/cmd/internal/rsc.io/arm/armasm/decode_test.go new file mode 100644 index 0000000000..25a345a882 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/decode_test.go @@ -0,0 +1,69 @@ +// Copyright 2014 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 armasm + +import ( + "encoding/hex" + "io/ioutil" + "strconv" + "strings" + "testing" +) + +func TestDecode(t *testing.T) { + data, err := ioutil.ReadFile("testdata/decode.txt") + if err != nil { + t.Fatal(err) + } + all := string(data) + for strings.Contains(all, "\t\t") { + all = strings.Replace(all, "\t\t", "\t", -1) + } + for _, line := range strings.Split(all, "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + f := strings.SplitN(line, "\t", 4) + i := strings.Index(f[0], "|") + if i < 0 { + t.Errorf("parsing %q: missing | separator", f[0]) + continue + } + if i%2 != 0 { + t.Errorf("parsing %q: misaligned | separator", f[0]) + } + size := i / 2 + code, err := hex.DecodeString(f[0][:i] + f[0][i+1:]) + if err != nil { + t.Errorf("parsing %q: %v", f[0], err) + continue + } + mode, err := strconv.Atoi(f[1]) + if err != nil { + t.Errorf("invalid mode %q in: %s", f[1], line) + continue + } + syntax, asm := f[2], f[3] + inst, err := Decode(code, Mode(mode)) + var out string + if err != nil { + out = "error: " + err.Error() + } else { + switch syntax { + case "gnu": + out = GNUSyntax(inst) + case "plan9": + out = Plan9Syntax(inst, 0, nil, nil) + default: + t.Errorf("unknown syntax %q", syntax) + continue + } + } + if out != asm || inst.Len != size { + t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size) + } + } +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/ext_test.go b/src/cmd/internal/rsc.io/arm/armasm/ext_test.go new file mode 100644 index 0000000000..b0bd855970 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/ext_test.go @@ -0,0 +1,614 @@ +// Copyright 2014 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. + +// Support for testing against external disassembler program. +// Copied and simplified from rsc.io/x86/x86asm/ext_test.go. + +package armasm + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "log" + "math/rand" + "os" + "os/exec" + "regexp" + "runtime" + "strings" + "testing" + "time" +) + +var ( + printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths") + dumpTest = flag.Bool("dump", false, "dump all encodings") + mismatch = flag.Bool("mismatch", false, "log allowed mismatches") + longTest = flag.Bool("long", false, "long test") + keep = flag.Bool("keep", false, "keep object files around") + debug = false +) + +// A ExtInst represents a single decoded instruction parsed +// from an external disassembler's output. +type ExtInst struct { + addr uint32 + enc [4]byte + nenc int + text string +} + +func (r ExtInst) String() string { + return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text) +} + +// An ExtDis is a connection between an external disassembler and a test. +type ExtDis struct { + Arch Mode + Dec chan ExtInst + File *os.File + Size int + KeepFile bool + Cmd *exec.Cmd +} + +// Run runs the given command - the external disassembler - and returns +// a buffered reader of its standard output. +func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) { + if *keep { + log.Printf("%s\n", strings.Join(cmd, " ")) + } + ext.Cmd = exec.Command(cmd[0], cmd[1:]...) + out, err := ext.Cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("stdoutpipe: %v", err) + } + if err := ext.Cmd.Start(); err != nil { + return nil, fmt.Errorf("exec: %v", err) + } + + b := bufio.NewReaderSize(out, 1<<20) + return b, nil +} + +// Wait waits for the command started with Run to exit. +func (ext *ExtDis) Wait() error { + return ext.Cmd.Wait() +} + +// testExtDis tests a set of byte sequences against an external disassembler. +// The disassembler is expected to produce the given syntax and be run +// in the given architecture mode (16, 32, or 64-bit). +// The extdis function must start the external disassembler +// and then parse its output, sending the parsed instructions on ext.Dec. +// The generate function calls its argument f once for each byte sequence +// to be tested. The generate function itself will be called twice, and it must +// make the same sequence of calls to f each time. +// When a disassembly does not match the internal decoding, +// allowedMismatch determines whether this mismatch should be +// allowed, or else considered an error. +func testExtDis( + t *testing.T, + syntax string, + arch Mode, + extdis func(ext *ExtDis) error, + generate func(f func([]byte)), + allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool, +) { + start := time.Now() + ext := &ExtDis{ + Dec: make(chan ExtInst), + Arch: arch, + } + errc := make(chan error) + + // First pass: write instructions to input file for external disassembler. + file, f, size, err := writeInst(generate) + if err != nil { + t.Fatal(err) + } + ext.Size = size + ext.File = f + defer func() { + f.Close() + if !*keep { + os.Remove(file) + } + }() + + // Second pass: compare disassembly against our decodings. + var ( + totalTests = 0 + totalSkips = 0 + totalErrors = 0 + + errors = make([]string, 0, 100) // sampled errors, at most cap + ) + go func() { + errc <- extdis(ext) + }() + generate(func(enc []byte) { + dec, ok := <-ext.Dec + if !ok { + t.Errorf("decoding stream ended early") + return + } + inst, text := disasm(syntax, arch, pad(enc)) + totalTests++ + if *dumpTest { + fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc) + } + if text != dec.text || inst.Len != dec.nenc { + suffix := "" + if allowedMismatch(text, size, &inst, dec) { + totalSkips++ + if !*mismatch { + return + } + suffix += " (allowed mismatch)" + } + totalErrors++ + if len(errors) >= cap(errors) { + j := rand.Intn(totalErrors) + if j >= cap(errors) { + return + } + errors = append(errors[:j], errors[j+1:]...) + } + errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix)) + } + }) + + if *mismatch { + totalErrors -= totalSkips + } + + for _, b := range errors { + t.Log(b) + } + + if totalErrors > 0 { + t.Fail() + } + t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds()) + + if err := <-errc; err != nil { + t.Fatal("external disassembler: %v", err) + } + +} + +const start = 0x8000 // start address of text + +// writeInst writes the generated byte sequences to a new file +// starting at offset start. That file is intended to be the input to +// the external disassembler. +func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) { + f, err = ioutil.TempFile("", "armasm") + if err != nil { + return + } + + file = f.Name() + + f.Seek(start, 0) + w := bufio.NewWriter(f) + defer w.Flush() + size = 0 + generate(func(x []byte) { + if len(x) > 4 { + x = x[:4] + } + if debug { + fmt.Printf("%#x: %x%x\n", start+size, x, zeros[len(x):]) + } + w.Write(x) + w.Write(zeros[len(x):]) + size += len(zeros) + }) + return file, f, size, nil +} + +var zeros = []byte{0, 0, 0, 0} + +// pad pads the code sequenc with pops. +func pad(enc []byte) []byte { + if len(enc) < 4 { + enc = append(enc[:len(enc):len(enc)], zeros[:4-len(enc)]...) + } + return enc +} + +// disasm returns the decoded instruction and text +// for the given source bytes, using the given syntax and mode. +func disasm(syntax string, mode Mode, src []byte) (inst Inst, text string) { + // If printTests is set, we record the coverage value + // before and after, and we write out the inputs for which + // coverage went up, in the format expected in testdata/decode.text. + // This produces a fairly small set of test cases that exercise nearly + // all the code. + var cover float64 + if *printTests { + cover -= coverage() + } + + inst, err := Decode(src, mode) + if err != nil { + text = "error: " + err.Error() + } else { + text = inst.String() + switch syntax { + //case "arm": + // text = ARMSyntax(inst) + case "gnu": + text = GNUSyntax(inst) + //case "plan9": + // text = Plan9Syntax(inst, 0, nil) + default: + text = "error: unknown syntax " + syntax + } + } + + if *printTests { + cover += coverage() + if cover > 0 { + max := len(src) + if max > 4 && inst.Len <= 4 { + max = 4 + } + fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text) + } + } + + return +} + +// coverage returns a floating point number denoting the +// test coverage until now. The number increases when new code paths are exercised, +// both in the Go program and in the decoder byte code. +func coverage() float64 { + /* + testing.Coverage is not in the main distribution. + The implementation, which must go in package testing, is: + + // Coverage reports the current code coverage as a fraction in the range [0, 1]. + func Coverage() float64 { + var n, d int64 + for _, counters := range cover.Counters { + for _, c := range counters { + if c > 0 { + n++ + } + d++ + } + } + if d == 0 { + return 0 + } + return float64(n) / float64(d) + } + */ + + var f float64 + f += testing.Coverage() + f += decodeCoverage() + return f +} + +func decodeCoverage() float64 { + n := 0 + for _, t := range decoderCover { + if t { + n++ + } + } + return float64(1+n) / float64(1+len(decoderCover)) +} + +// Helpers for writing disassembler output parsers. + +// hasPrefix reports whether any of the space-separated words in the text s +// begins with any of the given prefixes. +func hasPrefix(s string, prefixes ...string) bool { + for _, prefix := range prefixes { + for s := s; s != ""; { + if strings.HasPrefix(s, prefix) { + return true + } + i := strings.Index(s, " ") + if i < 0 { + break + } + s = s[i+1:] + } + } + return false +} + +// contains reports whether the text s contains any of the given substrings. +func contains(s string, substrings ...string) bool { + for _, sub := range substrings { + if strings.Contains(s, sub) { + return true + } + } + return false +} + +// isHex reports whether b is a hexadecimal character (0-9A-Fa-f). +func isHex(b byte) bool { return b == '0' || unhex[b] > 0 } + +// parseHex parses the hexadecimal byte dump in hex, +// appending the parsed bytes to raw and returning the updated slice. +// The returned bool signals whether any invalid hex was found. +// Spaces and tabs between bytes are okay but any other non-hex is not. +func parseHex(hex []byte, raw []byte) ([]byte, bool) { + hex = trimSpace(hex) + for j := 0; j < len(hex); { + for hex[j] == ' ' || hex[j] == '\t' { + j++ + } + if j >= len(hex) { + break + } + if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) { + return nil, false + } + raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]]) + j += 2 + } + return raw, true +} + +var unhex = [256]byte{ + '0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + '5': 5, + '6': 6, + '7': 7, + '8': 8, + '9': 9, + 'A': 10, + 'B': 11, + 'C': 12, + 'D': 13, + 'E': 14, + 'F': 15, + 'a': 10, + 'b': 11, + 'c': 12, + 'd': 13, + 'e': 14, + 'f': 15, +} + +// index is like bytes.Index(s, []byte(t)) but avoids the allocation. +func index(s []byte, t string) int { + i := 0 + for { + j := bytes.IndexByte(s[i:], t[0]) + if j < 0 { + return -1 + } + i = i + j + if i+len(t) > len(s) { + return -1 + } + for k := 1; k < len(t); k++ { + if s[i+k] != t[k] { + goto nomatch + } + } + return i + nomatch: + i++ + } +} + +// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s. +// If s must be rewritten, it is rewritten in place. +func fixSpace(s []byte) []byte { + s = trimSpace(s) + for i := 0; i < len(s); i++ { + if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' { + goto Fix + } + } + return s + +Fix: + b := s + w := 0 + for i := 0; i < len(s); i++ { + c := s[i] + if c == '\t' || c == '\n' { + c = ' ' + } + if c == ' ' && w > 0 && b[w-1] == ' ' { + continue + } + b[w] = c + w++ + } + if w > 0 && b[w-1] == ' ' { + w-- + } + return b[:w] +} + +// trimSpace trims leading and trailing space from s, returning a subslice of s. +func trimSpace(s []byte) []byte { + j := len(s) + for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') { + j-- + } + i := 0 + for i < j && (s[i] == ' ' || s[i] == '\t') { + i++ + } + return s[i:j] +} + +// pcrel matches instructions using relative addressing mode. +var ( + pcrel = regexp.MustCompile(`^((?:.* )?(?:b|bl)x?(?:eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)?) 0x([0-9a-f]+)$`) +) + +// Generators. +// +// The test cases are described as functions that invoke a callback repeatedly, +// with a new input sequence each time. These helpers make writing those +// a little easier. + +// condCases generates conditional instructions. +func condCases(t *testing.T) func(func([]byte)) { + return func(try func([]byte)) { + // All the strides are relatively prime to 2 and therefore to 2²⁸, + // so we will not repeat any instructions until we have tried all 2²⁸. + // Using a stride other than 1 is meant to visit the instructions in a + // pseudorandom order, which gives better variety in the set of + // test cases chosen by -printtests. + stride := uint32(10007) + n := 1 << 28 / 7 + if testing.Short() { + stride = 100003 + n = 1 << 28 / 1001 + } else if *longTest { + stride = 200000033 + n = 1 << 28 + } + x := uint32(0) + for i := 0; i < n; i++ { + enc := (x%15)<<28 | x&(1<<28-1) + try([]byte{byte(enc), byte(enc >> 8), byte(enc >> 16), byte(enc >> 24)}) + x += stride + } + } +} + +// uncondCases generates unconditional instructions. +func uncondCases(t *testing.T) func(func([]byte)) { + return func(try func([]byte)) { + condCases(t)(func(enc []byte) { + enc[3] |= 0xF0 + try(enc) + }) + } +} + +func countBits(x uint32) int { + n := 0 + for ; x != 0; x >>= 1 { + n += int(x & 1) + } + return n +} + +func expandBits(x, m uint32) uint32 { + var out uint32 + for i := uint(0); i < 32; i++ { + out >>= 1 + if m&1 != 0 { + out |= (x & 1) << 31 + x >>= 1 + } + m >>= 1 + } + return out +} + +func tryCondMask(mask, val uint32, try func([]byte)) { + n := countBits(^mask) + bits := uint32(0) + for i := 0; i < 1<> 8), byte(x >> 16), byte(x >> 24)}) + } +} + +// vfpCases generates VFP instructions. +func vfpCases(t *testing.T) func(func([]byte)) { + const ( + vfpmask uint32 = 0xFF00FE10 + vfp uint32 = 0x0E009A00 + ) + return func(try func([]byte)) { + tryCondMask(0xff00fe10, 0x0e009a00, try) // standard VFP instruction space + tryCondMask(0xffc00f7f, 0x0e000b10, try) // VFP MOV core reg to/from float64 half + tryCondMask(0xffe00f7f, 0x0e000a10, try) // VFP MOV core reg to/from float32 + tryCondMask(0xffef0fff, 0x0ee10a10, try) // VFP MOV core reg to/from cond codes + } +} + +// hexCases generates the cases written in hexadecimal in the encoded string. +// Spaces in 'encoded' separate entire test cases, not individual bytes. +func hexCases(t *testing.T, encoded string) func(func([]byte)) { + return func(try func([]byte)) { + for _, x := range strings.Fields(encoded) { + src, err := hex.DecodeString(x) + if err != nil { + t.Errorf("parsing %q: %v", x, err) + } + try(src) + } + } +} + +// testdataCases generates the test cases recorded in testdata/decode.txt. +// It only uses the inputs; it ignores the answers recorded in that file. +func testdataCases(t *testing.T) func(func([]byte)) { + var codes [][]byte + data, err := ioutil.ReadFile("testdata/decode.txt") + if err != nil { + t.Fatal(err) + } + for _, line := range strings.Split(string(data), "\n") { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + f := strings.Fields(line)[0] + i := strings.Index(f, "|") + if i < 0 { + t.Errorf("parsing %q: missing | separator", f) + continue + } + if i%2 != 0 { + t.Errorf("parsing %q: misaligned | separator", f) + } + code, err := hex.DecodeString(f[:i] + f[i+1:]) + if err != nil { + t.Errorf("parsing %q: %v", f, err) + continue + } + codes = append(codes, code) + } + + return func(try func([]byte)) { + for _, code := range codes { + try(code) + } + } +} + +func caller(skip int) string { + pc, _, _, _ := runtime.Caller(skip) + f := runtime.FuncForPC(pc) + name := "?" + if f != nil { + name = f.Name() + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[i+1:] + } + } + return name +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/gnu.go b/src/cmd/internal/rsc.io/arm/armasm/gnu.go new file mode 100644 index 0000000000..1a97a5a844 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/gnu.go @@ -0,0 +1,164 @@ +// Copyright 2014 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 armasm + +import ( + "bytes" + "fmt" + "strings" +) + +var saveDot = strings.NewReplacer( + ".F16", "_dot_F16", + ".F32", "_dot_F32", + ".F64", "_dot_F64", + ".S32", "_dot_S32", + ".U32", "_dot_U32", + ".FXS", "_dot_S", + ".FXU", "_dot_U", + ".32", "_dot_32", +) + +// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. +// This form typically matches the syntax defined in the ARM Reference Manual. +func GNUSyntax(inst Inst) string { + var buf bytes.Buffer + op := inst.Op.String() + op = saveDot.Replace(op) + op = strings.Replace(op, ".", "", -1) + op = strings.Replace(op, "_dot_", ".", -1) + op = strings.ToLower(op) + buf.WriteString(op) + sep := " " + for i, arg := range inst.Args { + if arg == nil { + break + } + text := gnuArg(&inst, i, arg) + if text == "" { + continue + } + buf.WriteString(sep) + sep = ", " + buf.WriteString(text) + } + return buf.String() +} + +func gnuArg(inst *Inst, argIndex int, arg Arg) string { + switch inst.Op &^ 15 { + case LDRD_EQ, LDREXD_EQ, STRD_EQ: + if argIndex == 1 { + // second argument in consecutive pair not printed + return "" + } + case STREXD_EQ: + if argIndex == 2 { + // second argument in consecutive pair not printed + return "" + } + } + + switch arg := arg.(type) { + case Imm: + switch inst.Op &^ 15 { + case BKPT_EQ: + return fmt.Sprintf("%#04x", uint32(arg)) + case SVC_EQ: + return fmt.Sprintf("%#08x", uint32(arg)) + } + return fmt.Sprintf("#%d", int32(arg)) + + case ImmAlt: + return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) + + case Mem: + R := gnuArg(inst, -1, arg.Base) + X := "" + if arg.Sign != 0 { + X = "" + if arg.Sign < 0 { + X = "-" + } + X += gnuArg(inst, -1, arg.Index) + if arg.Shift == ShiftLeft && arg.Count == 0 { + // nothing + } else if arg.Shift == RotateRightExt { + X += ", rrx" + } else { + X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) + } + } else { + X = fmt.Sprintf("#%d", arg.Offset) + } + + switch arg.Mode { + case AddrOffset: + if X == "#0" { + return fmt.Sprintf("[%s]", R) + } + return fmt.Sprintf("[%s, %s]", R, X) + case AddrPreIndex: + return fmt.Sprintf("[%s, %s]!", R, X) + case AddrPostIndex: + return fmt.Sprintf("[%s], %s", R, X) + case AddrLDM: + if X == "#0" { + return R + } + case AddrLDM_WB: + if X == "#0" { + return R + "!" + } + } + return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) + + case PCRel: + return fmt.Sprintf(".%+#x", int32(arg)+4) + + case Reg: + switch inst.Op &^ 15 { + case LDREX_EQ: + if argIndex == 0 { + return fmt.Sprintf("r%d", int32(arg)) + } + } + switch arg { + case R10: + return "sl" + case R11: + return "fp" + case R12: + return "ip" + } + + case RegList: + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if arg&(1<= Op(len(opstr)) || opstr[op] == "" { + return fmt.Sprintf("Op(%d)", int(op)) + } + return opstr[op] +} + +// An Inst is a single instruction. +type Inst struct { + Op Op // Opcode mnemonic + Enc uint32 // Raw encoding bits. + Len int // Length of encoding in bytes. + Args Args // Instruction arguments, in ARM manual order. +} + +func (i Inst) String() string { + var buf bytes.Buffer + buf.WriteString(i.Op.String()) + for j, arg := range i.Args { + if arg == nil { + break + } + if j == 0 { + buf.WriteString(" ") + } else { + buf.WriteString(", ") + } + buf.WriteString(arg.String()) + } + return buf.String() +} + +// An Args holds the instruction arguments. +// If an instruction has fewer than 4 arguments, +// the final elements in the array are nil. +type Args [4]Arg + +// An Arg is a single instruction argument, one of these types: +// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg. +type Arg interface { + IsArg() + String() string +} + +type Float32Imm float32 + +func (Float32Imm) IsArg() {} + +func (f Float32Imm) String() string { + return fmt.Sprintf("#%v", float32(f)) +} + +type Float64Imm float32 + +func (Float64Imm) IsArg() {} + +func (f Float64Imm) String() string { + return fmt.Sprintf("#%v", float64(f)) +} + +// An Imm is an integer constant. +type Imm uint32 + +func (Imm) IsArg() {} + +func (i Imm) String() string { + return fmt.Sprintf("#%#x", uint32(i)) +} + +// A ImmAlt is an alternate encoding of an integer constant. +type ImmAlt struct { + Val uint8 + Rot uint8 +} + +func (ImmAlt) IsArg() {} + +func (i ImmAlt) Imm() Imm { + v := uint32(i.Val) + r := uint(i.Rot) + return Imm(v>>r | v<<(32-r)) +} + +func (i ImmAlt) String() string { + return fmt.Sprintf("#%#x, %d", i.Val, i.Rot) +} + +// A Label is a text (code) address. +type Label uint32 + +func (Label) IsArg() {} + +func (i Label) String() string { + return fmt.Sprintf("%#x", uint32(i)) +} + +// A Reg is a single register. +// The zero value denotes R0, not the absence of a register. +type Reg uint8 + +const ( + R0 Reg = iota + R1 + R2 + R3 + R4 + R5 + R6 + R7 + R8 + R9 + R10 + R11 + R12 + R13 + R14 + R15 + + S0 + S1 + S2 + S3 + S4 + S5 + S6 + S7 + S8 + S9 + S10 + S11 + S12 + S13 + S14 + S15 + S16 + S17 + S18 + S19 + S20 + S21 + S22 + S23 + S24 + S25 + S26 + S27 + S28 + S29 + S30 + S31 + + D0 + D1 + D2 + D3 + D4 + D5 + D6 + D7 + D8 + D9 + D10 + D11 + D12 + D13 + D14 + D15 + D16 + D17 + D18 + D19 + D20 + D21 + D22 + D23 + D24 + D25 + D26 + D27 + D28 + D29 + D30 + D31 + + APSR + APSR_nzcv + FPSCR + + SP = R13 + LR = R14 + PC = R15 +) + +func (Reg) IsArg() {} + +func (r Reg) String() string { + switch r { + case APSR: + return "APSR" + case APSR_nzcv: + return "APSR_nzcv" + case FPSCR: + return "FPSCR" + case SP: + return "SP" + case PC: + return "PC" + case LR: + return "LR" + } + if R0 <= r && r <= R15 { + return fmt.Sprintf("R%d", int(r-R0)) + } + if S0 <= r && r <= S31 { + return fmt.Sprintf("S%d", int(r-S0)) + } + if D0 <= r && r <= D31 { + return fmt.Sprintf("D%d", int(r-D0)) + } + return fmt.Sprintf("Reg(%d)", int(r)) +} + +// A RegX represents a fraction of a multi-value register. +// The Index field specifies the index number, +// but the size of the fraction is not specified. +// It must be inferred from the instruction and the register type. +// For example, in a VMOV instruction, RegX{D5, 1} represents +// the top 32 bits of the 64-bit D5 register. +type RegX struct { + Reg Reg + Index int +} + +func (RegX) IsArg() {} + +func (r RegX) String() string { + return fmt.Sprintf("%s[%d]", r.Reg, r.Index) +} + +// A RegList is a register list. +// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list. +type RegList uint16 + +func (RegList) IsArg() {} + +func (r RegList) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "{") + sep := "" + for i := 0; i < 16; i++ { + if r&(1<= 4 { + raw := binary.LittleEndian.Uint32(dec.enc[:4]) + + // word 21FFF0B5. + // the manual is clear that this is pre-indexed mode (with !) but libopcodes generates post-index (without !). + if raw&0x01200000 == 0x01200000 && strings.Replace(text, "!", "", -1) == dec.text { + return true + } + + // word C100543E: libopcodes says tst, but no evidence for that. + if strings.HasPrefix(dec.text, "tst") && raw&0x0ff00000 != 0x03100000 && raw&0x0ff00000 != 0x01100000 { + return true + } + + // word C3203CE8: libopcodes says teq, but no evidence for that. + if strings.HasPrefix(dec.text, "teq") && raw&0x0ff00000 != 0x03300000 && raw&0x0ff00000 != 0x01300000 { + return true + } + + // word D14C552E: libopcodes says cmp but no evidence for that. + if strings.HasPrefix(dec.text, "cmp") && raw&0x0ff00000 != 0x03500000 && raw&0x0ff00000 != 0x01500000 { + return true + } + + // word 2166AA4A: libopcodes says cmn but no evidence for that. + if strings.HasPrefix(dec.text, "cmn") && raw&0x0ff00000 != 0x03700000 && raw&0x0ff00000 != 0x01700000 { + return true + } + + // word E70AEEEF: libopcodes says str but no evidence for that. + if strings.HasPrefix(dec.text, "str") && len(dec.text) >= 5 && (dec.text[3] == ' ' || dec.text[5] == ' ') && raw&0x0e500018 != 0x06000000 && raw&0x0e500000 != 0x0400000 { + return true + } + + // word B0AF48F4: libopcodes says strd but P=0,W=1 which is unpredictable. + if hasPrefix(dec.text, "ldr", "str") && raw&0x01200000 == 0x00200000 { + return true + } + + // word B6CC1C76: libopcodes inexplicably says 'uxtab16lt r1, ip, r6, ROR #24' instead of 'uxtab16lt r1, ip, r6, ror #24' + if strings.ToLower(dec.text) == text { + return true + } + + // word F410FDA1: libopcodes says PLDW but the manual is clear that PLDW is F5/F7, not F4. + // word F7D0FB17: libopcodes says PLDW but the manual is clear that PLDW has 0x10 clear + if hasPrefix(dec.text, "pld") && raw&0xfd000010 != 0xf5000000 { + return true + } + + // word F650FE14: libopcodes says PLI but the manual is clear that PLI has 0x10 clear + if hasPrefix(dec.text, "pli") && raw&0xff000010 != 0xf6000000 { + return true + } + } + + return false +} + +// Instructions known to libopcodes (or xed) but not to us. +// Most of these are floating point coprocessor instructions. +var unsupported = strings.Fields(` + abs + acs + adf + aes + asn + atn + cdp + cf + cmf + cnf + cos + cps + crc32 + dvf + eret + exp + fadd + fcmp + fcpy + fcvt + fdiv + fdv + fix + fld + flt + fmac + fmd + fml + fmr + fms + fmul + fmx + fneg + fnm + frd + fsit + fsq + fst + fsu + fto + fui + hlt + hvc + lda + ldc + ldf + lfm + lgn + log + mar + mcr + mcrr + mia + mnf + mra + mrc + mrrc + mrs + msr + msr + muf + mvf + nrm + pol + pow + rdf + rfc + rfe + rfs + rmf + rnd + rpw + rsf + sdiv + sev + sfm + sha1 + sha256 + sin + smc + sqt + srs + stc + stf + stl + suf + tan + udf + udiv + urd + vfma + vfms + vfnma + vfnms + vrint + wfc + wfs +`) diff --git a/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go b/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go new file mode 100644 index 0000000000..d88c67fc05 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/objdumpext_test.go @@ -0,0 +1,260 @@ +// Copyright 2014 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. + +// Copied and simplified from rsc.io/x86/x86asm/objdumpext_test.go. + +package armasm + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "testing" +) + +const objdumpPath = "/usr/local/bin/arm-linux-elf-objdump" + +func testObjdumpARM(t *testing.T, generate func(func([]byte))) { + testObjdumpArch(t, generate, ModeARM) +} + +func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch Mode) { + if testing.Short() { + t.Skip("skipping objdump test in short mode") + } + + if _, err := os.Stat(objdumpPath); err != nil { + t.Fatal(err) + } + + testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump) +} + +func objdump(ext *ExtDis) error { + // File already written with instructions; add ELF header. + if ext.Arch == ModeARM { + if err := writeELF32(ext.File, ext.Size); err != nil { + return err + } + } else { + panic("unknown arch") + } + + b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name()) + if err != nil { + return err + } + + var ( + nmatch int + reading bool + next uint32 = start + addr uint32 + encbuf [4]byte + enc []byte + text string + ) + flush := func() { + if addr == next { + if m := pcrel.FindStringSubmatch(text); m != nil { + targ, _ := strconv.ParseUint(m[2], 16, 64) + text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc)))) + } + if strings.HasPrefix(text, "stmia") { + text = "stm" + text[5:] + } + if strings.HasPrefix(text, "stmfd") { + text = "stmdb" + text[5:] + } + if strings.HasPrefix(text, "ldmfd") { + text = "ldm" + text[5:] + } + text = strings.Replace(text, "#0.0", "#0", -1) + if text == "undefined" && len(enc) == 4 { + text = "error: unknown instruction" + enc = nil + } + if len(enc) == 4 { + // prints as word but we want to record bytes + enc[0], enc[3] = enc[3], enc[0] + enc[1], enc[2] = enc[2], enc[1] + } + ext.Dec <- ExtInst{addr, encbuf, len(enc), text} + encbuf = [4]byte{} + enc = nil + next += 4 + } + } + var textangle = []byte("<.text>:") + for { + line, err := b.ReadSlice('\n') + if err != nil { + if err == io.EOF { + break + } + return fmt.Errorf("reading objdump output: %v", err) + } + if bytes.Contains(line, textangle) { + reading = true + continue + } + if !reading { + continue + } + if debug { + os.Stdout.Write(line) + } + if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil { + enc = enc1 + continue + } + flush() + nmatch++ + addr, enc, text = parseLine(line, encbuf[:0]) + if addr > next { + return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line) + } + } + flush() + if next != start+uint32(ext.Size) { + return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size) + } + if err := ext.Wait(); err != nil { + return fmt.Errorf("exec: %v", err) + } + + return nil +} + +var ( + undefined = []byte("") + unpredictable = []byte("") + illegalShifter = []byte("") +) + +func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) { + oline := line + i := index(line, ":\t") + if i < 0 { + log.Fatalf("cannot parse disassembly: %q", oline) + } + x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32) + if err != nil { + log.Fatalf("cannot parse disassembly: %q", oline) + } + addr = uint32(x) + line = line[i+2:] + i = bytes.IndexByte(line, '\t') + if i < 0 { + log.Fatalf("cannot parse disassembly: %q", oline) + } + enc, ok := parseHex(line[:i], encstart) + if !ok { + log.Fatalf("cannot parse disassembly: %q", oline) + } + line = trimSpace(line[i:]) + if bytes.Contains(line, undefined) { + text = "undefined" + return + } + if bytes.Contains(line, illegalShifter) { + text = "undefined" + return + } + if false && bytes.Contains(line, unpredictable) { + text = "unpredictable" + return + } + if i := bytes.IndexByte(line, ';'); i >= 0 { + line = trimSpace(line[:i]) + } + text = string(fixSpace(line)) + return +} + +func parseContinuation(line []byte, enc []byte) []byte { + i := index(line, ":\t") + if i < 0 { + return nil + } + line = line[i+1:] + enc, _ = parseHex(line, enc) + return enc +} + +// writeELF32 writes an ELF32 header to the file, +// describing a text segment that starts at start +// and extends for size bytes. +func writeELF32(f *os.File, size int) error { + f.Seek(0, 0) + var hdr elf.Header32 + var prog elf.Prog32 + var sect elf.Section32 + var buf bytes.Buffer + binary.Write(&buf, binary.LittleEndian, &hdr) + off1 := buf.Len() + binary.Write(&buf, binary.LittleEndian, &prog) + off2 := buf.Len() + binary.Write(&buf, binary.LittleEndian, §) + off3 := buf.Len() + buf.Reset() + data := byte(elf.ELFDATA2LSB) + hdr = elf.Header32{ + Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1}, + Type: 2, + Machine: uint16(elf.EM_ARM), + Version: 1, + Entry: start, + Phoff: uint32(off1), + Shoff: uint32(off2), + Flags: 0x05000002, + Ehsize: uint16(off1), + Phentsize: uint16(off2 - off1), + Phnum: 1, + Shentsize: uint16(off3 - off2), + Shnum: 3, + Shstrndx: 2, + } + binary.Write(&buf, binary.LittleEndian, &hdr) + prog = elf.Prog32{ + Type: 1, + Off: start, + Vaddr: start, + Paddr: start, + Filesz: uint32(size), + Memsz: uint32(size), + Flags: 5, + Align: start, + } + binary.Write(&buf, binary.LittleEndian, &prog) + binary.Write(&buf, binary.LittleEndian, §) // NULL section + sect = elf.Section32{ + Name: 1, + Type: uint32(elf.SHT_PROGBITS), + Addr: start, + Off: start, + Size: uint32(size), + Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR), + Addralign: 4, + } + binary.Write(&buf, binary.LittleEndian, §) // .text + sect = elf.Section32{ + Name: uint32(len("\x00.text\x00")), + Type: uint32(elf.SHT_STRTAB), + Addr: 0, + Off: uint32(off2 + (off3-off2)*3), + Size: uint32(len("\x00.text\x00.shstrtab\x00")), + Addralign: 1, + } + binary.Write(&buf, binary.LittleEndian, §) + buf.WriteString("\x00.text\x00.shstrtab\x00") + f.Write(buf.Bytes()) + return nil +} diff --git a/src/cmd/internal/rsc.io/arm/armasm/plan9x.go b/src/cmd/internal/rsc.io/arm/armasm/plan9x.go new file mode 100644 index 0000000000..952c5190b6 --- /dev/null +++ b/src/cmd/internal/rsc.io/arm/armasm/plan9x.go @@ -0,0 +1,211 @@ +// Copyright 2014 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 armasm + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "strings" +) + +// Plan9Syntax returns the Go assembler syntax for the instruction. +// The syntax was originally defined by Plan 9. +// The pc is the program counter of the instruction, used for expanding +// PC-relative addresses into absolute ones. +// The symname function queries the symbol table for the program +// being disassembled. Given a target address it returns the name and base +// address of the symbol containing the target, if any; otherwise it returns "", 0. +// The reader r should read from the text segment using text addresses +// as offsets; it is used to display pc-relative loads as constant loads. +func Plan9Syntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { + if symname == nil { + symname = func(uint64) (string, uint64) { return "", 0 } + } + + var args []string + for _, a := range inst.Args { + if a == nil { + break + } + args = append(args, plan9Arg(&inst, pc, symname, a)) + } + + op := inst.Op.String() + + switch inst.Op &^ 15 { + case LDR_EQ, LDRB_EQ, LDRH_EQ: + // Check for RET + reg, _ := inst.Args[0].(Reg) + mem, _ := inst.Args[1].(Mem) + if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex { + return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset) + } + + // Check for PC-relative load. + if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil { + addr := uint32(pc) + 8 + uint32(mem.Offset) + buf := make([]byte, 4) + switch inst.Op &^ 15 { + case LDRB_EQ: + if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil { + break + } + args[1] = fmt.Sprintf("$%#x", buf[0]) + + case LDRH_EQ: + if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil { + break + } + args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf)) + + case LDR_EQ: + if _, err := text.ReadAt(buf, int64(addr)); err != nil { + break + } + x := binary.LittleEndian.Uint32(buf) + if s, base := symname(uint64(x)); s != "" && uint64(x) == base { + args[1] = fmt.Sprintf("$%s(SB)", s) + } else { + args[1] = fmt.Sprintf("$%#x", x) + } + } + } + } + + // Move addressing mode into opcode suffix. + suffix := "" + switch inst.Op &^ 15 { + case LDR_EQ, LDRB_EQ, LDRH_EQ, STR_EQ, STRB_EQ, STRH_EQ: + mem, _ := inst.Args[1].(Mem) + switch mem.Mode { + case AddrOffset, AddrLDM: + // no suffix + case AddrPreIndex, AddrLDM_WB: + suffix = ".W" + case AddrPostIndex: + suffix = ".P" + } + off := "" + if mem.Offset != 0 { + off = fmt.Sprintf("%#x", mem.Offset) + } + base := fmt.Sprintf("(R%d)", int(mem.Base)) + index := "" + if mem.Sign != 0 { + sign := "" + if mem.Sign < 0 { + sign = "" + } + shift := "" + if mem.Count != 0 { + shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count) + } + index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift) + } + args[1] = off + base + index + } + + // Reverse args, placing dest last. + for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { + args[i], args[j] = args[j], args[i] + } + + switch inst.Op &^ 15 { + case MOV_EQ: + op = "MOVW" + op[3:] + + case LDR_EQ: + op = "MOVW" + op[3:] + suffix + case LDRB_EQ: + op = "MOVB" + op[4:] + suffix + case LDRH_EQ: + op = "MOVH" + op[4:] + suffix + + case STR_EQ: + op = "MOVW" + op[3:] + suffix + args[0], args[1] = args[1], args[0] + case STRB_EQ: + op = "MOVB" + op[4:] + suffix + args[0], args[1] = args[1], args[0] + case STRH_EQ: + op = "MOVH" + op[4:] + suffix + args[0], args[1] = args[1], args[0] + } + + if args != nil { + op += " " + strings.Join(args, ", ") + } + + return op +} + +// assembler syntax for the various shifts. +// @x> is a lie; the assembler uses @> 0 +// instead of @x> 1, but i wanted to be clear that it +// was a different operation (rotate right extended, not rotate right). +var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"} + +func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { + switch a := arg.(type) { + case Endian: + + case Imm: + return fmt.Sprintf("$%d", int(a)) + + case Mem: + + case PCRel: + addr := uint32(pc) + 8 + uint32(a) + if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { + return fmt.Sprintf("%s(SB)", s) + } + return fmt.Sprintf("%#x", addr) + + case Reg: + if a < 16 { + return fmt.Sprintf("R%d", int(a)) + } + + case RegList: + var buf bytes.Buffer + start := -2 + end := -2 + fmt.Fprintf(&buf, "[") + flush := func() { + if start >= 0 { + if buf.Len() > 1 { + fmt.Fprintf(&buf, ",") + } + if start == end { + fmt.Fprintf(&buf, "R%d", start) + } else { + fmt.Fprintf(&buf, "R%d-R%d", start, end) + } + } + } + for i := 0; i < 16; i++ { + if a&(1< ,,# cond:4|0|0|1|0|1|0|1|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00a00010, 4, ADC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // ADC{S} ,,, cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00a00000, 2, ADC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // ADC{S} ,,{,} cond:4|0|0|0|0|1|0|1|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02800000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // ADD{S} ,,# cond:4|0|0|1|0|1|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00800010, 4, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // ADD{S} ,,, cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00800000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // ADD{S} ,,{,} cond:4|0|0|0|0|1|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0000, 0x028d0000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_SP, arg_const}}, // ADD{S} ,SP,# cond:4|0|0|1|0|1|0|0|S|1|1|0|1|Rd:4|imm12:12 + {0x0fef0010, 0x008d0000, 2, ADD_EQ, 0x14011c04, instArgs{arg_R_12, arg_SP, arg_R_shift_imm}}, // ADD{S} ,SP,{,} cond:4|0|0|0|0|1|0|0|S|1|1|0|1|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fe00000, 0x02000000, 2, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // AND{S} ,,# cond:4|0|0|1|0|0|0|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x00000010, 4, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // AND{S} ,,, cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x00000000, 2, AND_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // AND{S} ,,{,} cond:4|0|0|0|0|0|0|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0fef0070, 0x01a00040, 4, ASR_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_0, arg_imm5_32}}, // ASR{S} ,,# cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|imm5:5|1|0|0|Rm:4 + {0x0fef00f0, 0x01a00050, 4, ASR_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_0, arg_R_8}}, // ASR{S} ,, cond:4|0|0|0|1|1|0|1|S|0|0|0|0|Rd:4|Rm:4|0|1|0|1|Rn:4 + {0x0f000000, 0x0a000000, 4, B_EQ, 0x1c04, instArgs{arg_label24}}, // B cond:4|1|0|1|0|imm24:24 + {0x0fe0007f, 0x07c0001f, 4, BFC_EQ, 0x1c04, instArgs{arg_R_12, arg_imm5, arg_lsb_width}}, // BFC ,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|1|1|1|1 + {0x0fe00070, 0x07c00010, 2, BFI_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0, arg_imm5, arg_lsb_width}}, // BFI ,,#,# cond:4|0|1|1|1|1|1|0|msb:5|Rd:4|lsb:5|0|0|1|Rn:4 + {0x0fe00000, 0x03c00000, 2, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_const}}, // BIC{S} ,,# cond:4|0|0|1|1|1|1|0|S|Rn:4|Rd:4|imm12:12 + {0x0fe00090, 0x01c00010, 4, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_R}}, // BIC{S} ,,, cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|Rs:4|0|type:2|1|Rm:4 + {0x0fe00010, 0x01c00000, 2, BIC_EQ, 0x14011c04, instArgs{arg_R_12, arg_R_16, arg_R_shift_imm}}, // BIC{S} ,,{,} cond:4|0|0|0|1|1|1|0|S|Rn:4|Rd:4|imm5:5|type:2|0|Rm:4 + {0x0ff000f0, 0x01200070, 4, BKPT_EQ, 0x1c04, instArgs{arg_imm_12at8_4at0}}, // BKPT # cond:4|0|0|0|1|0|0|1|0|imm12:12|0|1|1|1|imm4:4 + {0x0f000000, 0x0b000000, 4, BL_EQ, 0x1c04, instArgs{arg_label24}}, // BL cond:4|1|0|1|1|imm24:24 + {0xfe000000, 0xfa000000, 4, BLX, 0x0, instArgs{arg_label24H}}, // BLX 1|1|1|1|1|0|1|H|imm24:24 + {0x0ffffff0, 0x012fff30, 4, BLX_EQ, 0x1c04, instArgs{arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ff000f0, 0x012fff30, 3, BLX_EQ, 0x1c04, instArgs{arg_R_0}}, // BLX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|1|Rm:4 + {0x0ffffff0, 0x012fff10, 4, BX_EQ, 0x1c04, instArgs{arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x012fff10, 3, BX_EQ, 0x1c04, instArgs{arg_R_0}}, // BX cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ffffff0, 0x012fff20, 4, BXJ_EQ, 0x1c04, instArgs{arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0x0ff000f0, 0x012fff20, 3, BXJ_EQ, 0x1c04, instArgs{arg_R_0}}, // BXJ cond:4|0|0|0|1|0|0|1|0|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|0|0|1|0|Rm:4 + {0xffffffff, 0xf57ff01f, 4, CLREX, 0x0, instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0xfff000f0, 0xf57ff01f, 3, CLREX, 0x0, instArgs{}}, // CLREX 1|1|1|1|0|1|0|1|0|1|1|1|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(1)|(0)|(0)|(0)|(0)|0|0|0|1|(1)|(1)|(1)|(1) + {0x0fff0ff0, 0x016f0f10, 4, CLZ_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff000f0, 0x016f0f10, 3, CLZ_EQ, 0x1c04, instArgs{arg_R_12, arg_R_0}}, // CLZ , cond:4|0|0|0|1|0|1|1|0|(1)|(1)|(1)|(1)|Rd:4|(1)|(1)|(1)|(1)|0|0|0|1|Rm:4 + {0x0ff0f000, 0x03700000, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03700000, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMN ,# cond:4|0|0|1|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01700010, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01700010, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMN ,, cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01700000, 4, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01700000, 3, CMN_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMN ,{,} cond:4|0|0|0|1|0|1|1|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff0f000, 0x03500000, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff00000, 0x03500000, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_const}}, // CMP ,# cond:4|0|0|1|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm12:12 + {0x0ff0f090, 0x01500010, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff00090, 0x01500010, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_R}}, // CMP ,, cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|Rs:4|0|type:2|1|Rm:4 + {0x0ff0f010, 0x01500000, 4, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ff00010, 0x01500000, 3, CMP_EQ, 0x1c04, instArgs{arg_R_16, arg_R_shift_imm}}, // CMP ,{,} cond:4|0|0|0|1|0|1|0|1|Rn:4|(0)|(0)|(0)|(0)|imm5:5|type:2|0|Rm:4 + {0x0ffffff0, 0x0320f0f0, 4, DBG_EQ, 0x1c04, instArgs{arg_option}}, // DBG #