2016-03-11 14:28:16 -08:00
|
|
|
// Copyright 2009 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.
|
|
|
|
|
|
|
|
|
|
//go:generate go run mkbuiltin.go
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
2016-06-24 15:03:04 -07:00
|
|
|
"bytes"
|
2016-03-11 14:28:16 -08:00
|
|
|
"cmd/compile/internal/ssa"
|
|
|
|
|
"cmd/internal/obj"
|
2016-04-06 12:01:40 -07:00
|
|
|
"cmd/internal/sys"
|
2016-03-11 14:28:16 -08:00
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
2016-03-21 14:37:57 +11:00
|
|
|
"runtime"
|
2016-03-11 14:28:16 -08:00
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var imported_unsafe bool
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
buildid string
|
2016-05-06 23:17:29 -07:00
|
|
|
|
|
|
|
|
flag_newparser bool
|
2016-03-11 14:28:16 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
cmd/compile: ignore OXXX nodes in closure captured vars list
Added a debug flag "-d closure" to explain compilation of
closures (should this be done some other way? Should we
rewrite the "-m" flag to "-d escapes"?) Used this to
discover that cause was an OXXX node in the captured vars
list, and in turn noticed that OXXX nodes are explicitly
ignored in all other processing of captured variables.
Couldn't figure out a reproducer, did verify that this OXXX
was not caused by an unnamed return value (which is one use
of these). Verified lack of heap allocation by examining -S
output.
Assembly:
(runtime/mgc.go:1371) PCDATA $0, $2
(runtime/mgc.go:1371) CALL "".notewakeup(SB)
(runtime/mgc.go:1377) LEAQ "".gcBgMarkWorker.func1·f(SB), AX
(runtime/mgc.go:1404) MOVQ AX, (SP)
(runtime/mgc.go:1404) MOVQ "".autotmp_2242+88(SP), CX
(runtime/mgc.go:1404) MOVQ CX, 8(SP)
(runtime/mgc.go:1404) LEAQ go.string."GC worker (idle)"(SB), AX
(runtime/mgc.go:1404) MOVQ AX, 16(SP)
(runtime/mgc.go:1404) MOVQ $16, 24(SP)
(runtime/mgc.go:1404) MOVB $20, 32(SP)
(runtime/mgc.go:1404) MOVQ $0, 40(SP)
(runtime/mgc.go:1404) PCDATA $0, $2
(runtime/mgc.go:1404) CALL "".gopark(SB)
Added a check for compiling_runtime to ensure that this is
caught in the future. Added a test to test the check.
Verified that 1.5.3 did NOT reject the test case when
compiled with -+ flag, so this is not a recently added bug.
Cause of bug is two-part -- there was no leaking closure
detection ever, and instead it relied on capture-of-variables
to trigger compiling_runtime test, but closures improved in
1.5.3 so that mere capture of a value did not also capture
the variable, which thus allowed closures to escape, as well
as this case where the escape was spurious. In
fixedbugs/issue14999.go, compare messages for f and g;
1.5.3 would reject g, but not f. 1.4 rejects both because
1.4 heap-allocates parameter x for both.
Fixes #14999.
Change-Id: I40bcdd27056810628e96763a44f2acddd503aee1
Reviewed-on: https://go-review.googlesource.com/21322
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-03-30 14:14:00 -04:00
|
|
|
Debug_append int
|
|
|
|
|
Debug_closure int
|
|
|
|
|
Debug_panic int
|
|
|
|
|
Debug_slice int
|
|
|
|
|
Debug_wb int
|
2016-03-11 14:28:16 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Debug arguments.
|
|
|
|
|
// These can be specified with the -d flag, as in "-d nil"
|
|
|
|
|
// to set the debug_checknil variable. In general the list passed
|
|
|
|
|
// to -d can be comma-separated.
|
|
|
|
|
var debugtab = []struct {
|
|
|
|
|
name string
|
|
|
|
|
val *int
|
|
|
|
|
}{
|
|
|
|
|
{"append", &Debug_append}, // print information about append compilation
|
cmd/compile: ignore OXXX nodes in closure captured vars list
Added a debug flag "-d closure" to explain compilation of
closures (should this be done some other way? Should we
rewrite the "-m" flag to "-d escapes"?) Used this to
discover that cause was an OXXX node in the captured vars
list, and in turn noticed that OXXX nodes are explicitly
ignored in all other processing of captured variables.
Couldn't figure out a reproducer, did verify that this OXXX
was not caused by an unnamed return value (which is one use
of these). Verified lack of heap allocation by examining -S
output.
Assembly:
(runtime/mgc.go:1371) PCDATA $0, $2
(runtime/mgc.go:1371) CALL "".notewakeup(SB)
(runtime/mgc.go:1377) LEAQ "".gcBgMarkWorker.func1·f(SB), AX
(runtime/mgc.go:1404) MOVQ AX, (SP)
(runtime/mgc.go:1404) MOVQ "".autotmp_2242+88(SP), CX
(runtime/mgc.go:1404) MOVQ CX, 8(SP)
(runtime/mgc.go:1404) LEAQ go.string."GC worker (idle)"(SB), AX
(runtime/mgc.go:1404) MOVQ AX, 16(SP)
(runtime/mgc.go:1404) MOVQ $16, 24(SP)
(runtime/mgc.go:1404) MOVB $20, 32(SP)
(runtime/mgc.go:1404) MOVQ $0, 40(SP)
(runtime/mgc.go:1404) PCDATA $0, $2
(runtime/mgc.go:1404) CALL "".gopark(SB)
Added a check for compiling_runtime to ensure that this is
caught in the future. Added a test to test the check.
Verified that 1.5.3 did NOT reject the test case when
compiled with -+ flag, so this is not a recently added bug.
Cause of bug is two-part -- there was no leaking closure
detection ever, and instead it relied on capture-of-variables
to trigger compiling_runtime test, but closures improved in
1.5.3 so that mere capture of a value did not also capture
the variable, which thus allowed closures to escape, as well
as this case where the escape was spurious. In
fixedbugs/issue14999.go, compare messages for f and g;
1.5.3 would reject g, but not f. 1.4 rejects both because
1.4 heap-allocates parameter x for both.
Fixes #14999.
Change-Id: I40bcdd27056810628e96763a44f2acddd503aee1
Reviewed-on: https://go-review.googlesource.com/21322
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2016-03-30 14:14:00 -04:00
|
|
|
{"closure", &Debug_closure}, // print information about closure compilation
|
2016-03-11 14:28:16 -08:00
|
|
|
{"disablenil", &Disable_checknil}, // disable nil checks
|
|
|
|
|
{"gcprog", &Debug_gcprog}, // print dump of GC programs
|
|
|
|
|
{"nil", &Debug_checknil}, // print information about nil checks
|
|
|
|
|
{"panic", &Debug_panic}, // do not hide any compiler panic
|
|
|
|
|
{"slice", &Debug_slice}, // print information about slice compilation
|
|
|
|
|
{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
|
|
|
|
|
{"wb", &Debug_wb}, // print information about write barriers
|
|
|
|
|
{"export", &Debug_export}, // print export data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func usage() {
|
|
|
|
|
fmt.Printf("usage: compile [options] file.go...\n")
|
|
|
|
|
obj.Flagprint(1)
|
|
|
|
|
Exit(2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func hidePanic() {
|
|
|
|
|
if Debug_panic == 0 && nsavederrors+nerrors > 0 {
|
|
|
|
|
// If we've already complained about things
|
|
|
|
|
// in the program, don't bother complaining
|
|
|
|
|
// about a panic too; let the user clean up
|
|
|
|
|
// the code and try again.
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func doversion() {
|
|
|
|
|
p := obj.Expstring()
|
|
|
|
|
if p == "X:none" {
|
|
|
|
|
p = ""
|
|
|
|
|
}
|
|
|
|
|
sep := ""
|
|
|
|
|
if p != "" {
|
|
|
|
|
sep = " "
|
|
|
|
|
}
|
2016-09-09 08:13:16 -04:00
|
|
|
fmt.Printf("compile version %s%s%s\n", obj.Version, sep, p)
|
2016-03-11 14:28:16 -08:00
|
|
|
os.Exit(0)
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 15:31:49 -04:00
|
|
|
// supportsDynlink reports whether or not the code generator for the given
|
|
|
|
|
// architecture supports the -shared and -dynlink flags.
|
|
|
|
|
func supportsDynlink(arch *sys.Arch) bool {
|
|
|
|
|
return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-24 15:03:04 -07:00
|
|
|
// timing data for compiler phases
|
|
|
|
|
var timings Timings
|
|
|
|
|
var benchfile string
|
|
|
|
|
|
2016-03-11 14:28:16 -08:00
|
|
|
func Main() {
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "init")
|
|
|
|
|
|
2016-03-11 14:28:16 -08:00
|
|
|
defer hidePanic()
|
|
|
|
|
|
2016-04-06 12:01:40 -07:00
|
|
|
Ctxt = obj.Linknew(Thearch.LinkArch)
|
2016-09-15 15:45:10 +10:00
|
|
|
Ctxt.DiagFunc = yyerror
|
2016-09-13 14:05:14 -07:00
|
|
|
Ctxt.Bso = bufio.NewWriter(os.Stdout)
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
localpkg = mkpkg("")
|
|
|
|
|
localpkg.Prefix = "\"\""
|
|
|
|
|
|
|
|
|
|
// pseudo-package, for scoping
|
|
|
|
|
builtinpkg = mkpkg("go.builtin")
|
|
|
|
|
builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin
|
|
|
|
|
|
|
|
|
|
// pseudo-package, accessed by import "unsafe"
|
|
|
|
|
unsafepkg = mkpkg("unsafe")
|
|
|
|
|
unsafepkg.Name = "unsafe"
|
|
|
|
|
|
|
|
|
|
// real package, referred to by generated runtime calls
|
|
|
|
|
Runtimepkg = mkpkg("runtime")
|
|
|
|
|
Runtimepkg.Name = "runtime"
|
|
|
|
|
|
|
|
|
|
// pseudo-packages used in symbol tables
|
|
|
|
|
itabpkg = mkpkg("go.itab")
|
|
|
|
|
itabpkg.Name = "go.itab"
|
|
|
|
|
itabpkg.Prefix = "go.itab" // not go%2eitab
|
|
|
|
|
|
2016-03-17 06:18:13 -07:00
|
|
|
itablinkpkg = mkpkg("go.itablink")
|
|
|
|
|
itablinkpkg.Name = "go.itablink"
|
|
|
|
|
itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
|
|
|
|
|
|
2016-03-11 14:28:16 -08:00
|
|
|
trackpkg = mkpkg("go.track")
|
|
|
|
|
trackpkg.Name = "go.track"
|
|
|
|
|
trackpkg.Prefix = "go.track" // not go%2etrack
|
|
|
|
|
|
|
|
|
|
typepkg = mkpkg("type")
|
|
|
|
|
typepkg.Name = "type"
|
|
|
|
|
|
2016-04-19 08:31:04 -07:00
|
|
|
// pseudo-package used for map zero values
|
|
|
|
|
mappkg = mkpkg("go.map")
|
|
|
|
|
mappkg.Name = "go.map"
|
|
|
|
|
mappkg.Prefix = "go.map"
|
|
|
|
|
|
2016-09-09 08:13:16 -04:00
|
|
|
Nacl = obj.GOOS == "nacl"
|
2016-03-11 14:28:16 -08:00
|
|
|
if Nacl {
|
2016-04-13 18:37:18 -07:00
|
|
|
flag_largemodel = true
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("%", "debug non-static initializers", &Debug['%'])
|
|
|
|
|
obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A'])
|
|
|
|
|
obj.Flagcount("B", "disable bounds checking", &Debug['B'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("E", "debug symbol export", &Debug['E'])
|
|
|
|
|
obj.Flagfn1("I", "add `directory` to import search path", addidir)
|
|
|
|
|
obj.Flagcount("K", "debug missing line numbers", &Debug['K'])
|
|
|
|
|
obj.Flagcount("M", "debug move generation", &Debug['M'])
|
|
|
|
|
obj.Flagcount("N", "disable optimizations", &Debug['N'])
|
|
|
|
|
obj.Flagcount("P", "debug peephole optimizer", &Debug['P'])
|
|
|
|
|
obj.Flagcount("R", "debug register optimizer", &Debug['R'])
|
|
|
|
|
obj.Flagcount("S", "print assembly listing", &Debug['S'])
|
|
|
|
|
obj.Flagfn0("V", "print compiler version", doversion)
|
|
|
|
|
obj.Flagcount("W", "debug parse tree after type checking", &Debug['W'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
|
|
|
|
|
flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
|
|
|
|
|
flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
|
|
|
|
|
flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
|
|
|
|
|
obj.Flagcount("f", "debug stack frames", &Debug['f'])
|
|
|
|
|
obj.Flagcount("g", "debug code generation", &Debug['g'])
|
|
|
|
|
obj.Flagcount("h", "halt on error", &Debug['h'])
|
|
|
|
|
obj.Flagcount("i", "debug line number stack", &Debug['i'])
|
|
|
|
|
obj.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
|
|
|
|
|
obj.Flagcount("l", "disable inlining", &Debug['l'])
|
2016-04-26 21:50:59 -04:00
|
|
|
flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("live", "debug liveness analysis", &debuglive)
|
|
|
|
|
obj.Flagcount("m", "print optimization decisions", &Debug['m'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
|
2016-05-06 23:17:29 -07:00
|
|
|
flag.BoolVar(&flag_newparser, "newparser", false, "use new parser")
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
|
|
|
|
|
flag.StringVar(&outfile, "o", "", "write output to `file`")
|
|
|
|
|
flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
|
|
|
|
|
flag.BoolVar(&writearchive, "pack", false, "write package file instead of object file")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("r", "debug generated wrappers", &Debug['r'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&flag_race, "race", false, "enable race detector")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
|
|
|
|
|
flag.BoolVar(&safemode, "u", false, "reject unsafe code")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("v", "increase debug verbosity", &Debug['v'])
|
|
|
|
|
obj.Flagcount("w", "debug type checking", &Debug['w'])
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagcount("x", "debug lexer", &Debug['x'])
|
2016-04-13 18:37:18 -07:00
|
|
|
var flag_shared bool
|
2016-03-11 14:28:16 -08:00
|
|
|
var flag_dynlink bool
|
2016-04-07 15:31:49 -04:00
|
|
|
if supportsDynlink(Thearch.LinkArch.Arch) {
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
|
2016-04-07 15:31:49 -04:00
|
|
|
flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
2016-04-06 12:01:40 -07:00
|
|
|
if Thearch.LinkArch.Family == sys.AMD64 {
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
2016-04-13 18:37:18 -07:00
|
|
|
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
|
|
|
|
|
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
|
|
|
|
|
flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
|
2016-06-24 15:03:04 -07:00
|
|
|
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
|
2016-03-11 14:28:16 -08:00
|
|
|
obj.Flagparse(usage)
|
|
|
|
|
|
2016-04-13 18:41:59 -07:00
|
|
|
Ctxt.Flag_shared = flag_dynlink || flag_shared
|
2016-03-11 14:28:16 -08:00
|
|
|
Ctxt.Flag_dynlink = flag_dynlink
|
|
|
|
|
Ctxt.Flag_optimize = Debug['N'] == 0
|
|
|
|
|
|
|
|
|
|
Ctxt.Debugasm = int32(Debug['S'])
|
|
|
|
|
Ctxt.Debugvlog = int32(Debug['v'])
|
|
|
|
|
|
|
|
|
|
if flag.NArg() < 1 {
|
|
|
|
|
usage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
startProfile()
|
|
|
|
|
|
2016-04-13 18:37:18 -07:00
|
|
|
if flag_race {
|
2016-03-11 14:28:16 -08:00
|
|
|
racepkg = mkpkg("runtime/race")
|
|
|
|
|
racepkg.Name = "race"
|
|
|
|
|
}
|
2016-04-13 18:37:18 -07:00
|
|
|
if flag_msan {
|
2016-03-11 14:28:16 -08:00
|
|
|
msanpkg = mkpkg("runtime/msan")
|
|
|
|
|
msanpkg.Name = "msan"
|
|
|
|
|
}
|
2016-04-13 18:37:18 -07:00
|
|
|
if flag_race && flag_msan {
|
2016-03-11 14:28:16 -08:00
|
|
|
log.Fatal("cannot use both -race and -msan")
|
2016-04-13 18:37:18 -07:00
|
|
|
} else if flag_race || flag_msan {
|
2016-03-11 14:28:16 -08:00
|
|
|
instrumenting = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse -d argument
|
|
|
|
|
if debugstr != "" {
|
|
|
|
|
Split:
|
|
|
|
|
for _, name := range strings.Split(debugstr, ",") {
|
|
|
|
|
if name == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
val := 1
|
|
|
|
|
if i := strings.Index(name, "="); i >= 0 {
|
|
|
|
|
var err error
|
|
|
|
|
val, err = strconv.Atoi(name[i+1:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("invalid debug value %v", name)
|
|
|
|
|
}
|
|
|
|
|
name = name[:i]
|
|
|
|
|
}
|
|
|
|
|
for _, t := range debugtab {
|
|
|
|
|
if t.name == name {
|
|
|
|
|
if t.val != nil {
|
|
|
|
|
*t.val = val
|
|
|
|
|
continue Split
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// special case for ssa for now
|
|
|
|
|
if strings.HasPrefix(name, "ssa/") {
|
|
|
|
|
// expect form ssa/phase/flag
|
|
|
|
|
// e.g. -d=ssa/generic_cse/time
|
|
|
|
|
// _ in phase name also matches space
|
|
|
|
|
phase := name[4:]
|
|
|
|
|
flag := "debug" // default flag is debug
|
|
|
|
|
if i := strings.Index(phase, "/"); i >= 0 {
|
|
|
|
|
flag = phase[i+1:]
|
|
|
|
|
phase = phase[:i]
|
|
|
|
|
}
|
|
|
|
|
err := ssa.PhaseOption(phase, flag, val)
|
|
|
|
|
if err != "" {
|
|
|
|
|
log.Fatalf(err)
|
|
|
|
|
}
|
|
|
|
|
continue Split
|
|
|
|
|
}
|
|
|
|
|
log.Fatalf("unknown debug key -d %s\n", name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// enable inlining. for now:
|
|
|
|
|
// default: inlining on. (debug['l'] == 1)
|
|
|
|
|
// -l: inlining off (debug['l'] == 0)
|
|
|
|
|
// -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1)
|
|
|
|
|
if Debug['l'] <= 1 {
|
|
|
|
|
Debug['l'] = 1 - Debug['l']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Thearch.Betypeinit()
|
2016-04-06 12:01:40 -07:00
|
|
|
Widthint = Thearch.LinkArch.IntSize
|
|
|
|
|
Widthptr = Thearch.LinkArch.PtrSize
|
|
|
|
|
Widthreg = Thearch.LinkArch.RegSize
|
2016-03-11 14:28:16 -08:00
|
|
|
|
2016-03-11 15:22:21 -08:00
|
|
|
initUniverse()
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
blockgen = 1
|
|
|
|
|
dclcontext = PEXTERN
|
|
|
|
|
nerrors = 0
|
|
|
|
|
lexlineno = 1
|
|
|
|
|
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "loadsys")
|
2016-03-11 14:28:16 -08:00
|
|
|
loadsys()
|
|
|
|
|
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "parse")
|
|
|
|
|
lexlineno0 := lexlineno
|
2016-03-11 14:28:16 -08:00
|
|
|
for _, infile = range flag.Args() {
|
|
|
|
|
if trace && Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("--- %s ---\n", infile)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
linehistpush(infile)
|
|
|
|
|
block = 1
|
|
|
|
|
iota_ = -1000000
|
|
|
|
|
imported_unsafe = false
|
2016-05-06 23:17:29 -07:00
|
|
|
if flag_newparser {
|
|
|
|
|
parseFile(infile)
|
|
|
|
|
} else {
|
|
|
|
|
oldParseFile(infile)
|
|
|
|
|
}
|
2016-03-11 14:28:16 -08:00
|
|
|
if nsyntaxerrors != 0 {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Instead of converting EOF into '\n' in getc and count it as an extra line
|
|
|
|
|
// for the line history to work, and which then has to be corrected elsewhere,
|
|
|
|
|
// just add a line here.
|
|
|
|
|
lexlineno++
|
|
|
|
|
linehistpop()
|
|
|
|
|
}
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Stop()
|
|
|
|
|
timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
testdclstack()
|
|
|
|
|
mkpackage(localpkg.Name) // final import not used checks
|
2016-03-11 15:22:21 -08:00
|
|
|
finishUniverse()
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
typecheckok = true
|
|
|
|
|
if Debug['f'] != 0 {
|
|
|
|
|
frame(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process top-level declarations in phases.
|
|
|
|
|
|
|
|
|
|
// Phase 1: const, type, and names and types of funcs.
|
|
|
|
|
// This will gather all the information about types
|
|
|
|
|
// and methods but doesn't depend on any of it.
|
|
|
|
|
defercheckwidth()
|
|
|
|
|
|
|
|
|
|
// Don't use range--typecheck can add closures to xtop.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "typecheck", "top1")
|
2016-03-11 14:28:16 -08:00
|
|
|
for i := 0; i < len(xtop); i++ {
|
|
|
|
|
if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
|
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
|
|
|
xtop[i] = typecheck(xtop[i], Etop)
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Phase 2: Variable assignments.
|
|
|
|
|
// To check interface assignments, depends on phase 1.
|
|
|
|
|
|
|
|
|
|
// Don't use range--typecheck can add closures to xtop.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "typecheck", "top2")
|
2016-03-11 14:28:16 -08:00
|
|
|
for i := 0; i < len(xtop); i++ {
|
|
|
|
|
if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
|
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
|
|
|
xtop[i] = typecheck(xtop[i], Etop)
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
resumecheckwidth()
|
|
|
|
|
|
|
|
|
|
// Phase 3: Type check function bodies.
|
|
|
|
|
// Don't use range--typecheck can add closures to xtop.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "typecheck", "func")
|
|
|
|
|
var fcount int64
|
2016-03-11 14:28:16 -08:00
|
|
|
for i := 0; i < len(xtop); i++ {
|
|
|
|
|
if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
|
|
|
|
|
Curfn = xtop[i]
|
|
|
|
|
decldepth = 1
|
|
|
|
|
saveerrors()
|
2016-03-19 17:02:01 -07:00
|
|
|
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
2016-03-11 14:28:16 -08:00
|
|
|
checkreturn(Curfn)
|
|
|
|
|
if nerrors != 0 {
|
|
|
|
|
Curfn.Nbody.Set(nil) // type errors; do not compile
|
|
|
|
|
}
|
2016-06-24 15:03:04 -07:00
|
|
|
fcount++
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.AddEvent(fcount, "funcs")
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
// Phase 4: Decide how to capture closed variables.
|
|
|
|
|
// This needs to run before escape analysis,
|
|
|
|
|
// because variables captured by value do not escape.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "capturevars")
|
2016-03-11 14:28:16 -08:00
|
|
|
for _, n := range xtop {
|
|
|
|
|
if n.Op == ODCLFUNC && n.Func.Closure != nil {
|
|
|
|
|
Curfn = n
|
|
|
|
|
capturevars(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Curfn = nil
|
|
|
|
|
|
|
|
|
|
if nsavederrors+nerrors != 0 {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Phase 5: Inlining
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "inlining")
|
2016-03-11 14:28:16 -08:00
|
|
|
if Debug['l'] > 1 {
|
|
|
|
|
// Typecheck imported function bodies if debug['l'] > 1,
|
|
|
|
|
// otherwise lazily when used or re-exported.
|
|
|
|
|
for _, n := range importlist {
|
2016-04-24 13:50:26 -07:00
|
|
|
if n.Func.Inl.Len() != 0 {
|
2016-03-11 14:28:16 -08:00
|
|
|
saveerrors()
|
|
|
|
|
typecheckinl(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nsavederrors+nerrors != 0 {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if Debug['l'] != 0 {
|
|
|
|
|
// Find functions that can be inlined and clone them before walk expands them.
|
|
|
|
|
visitBottomUp(xtop, func(list []*Node, recursive bool) {
|
2016-03-23 16:35:50 +01:00
|
|
|
for _, n := range list {
|
2016-05-05 11:45:27 -07:00
|
|
|
if !recursive {
|
2016-03-11 14:28:16 -08:00
|
|
|
caninl(n)
|
2016-05-03 17:21:32 -07:00
|
|
|
} else {
|
|
|
|
|
if Debug['m'] > 1 {
|
|
|
|
|
fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
|
|
|
|
|
}
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
2016-05-05 11:45:27 -07:00
|
|
|
inlcalls(n)
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Phase 6: Escape analysis.
|
|
|
|
|
// 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.
|
|
|
|
|
// Large values are also moved off stack in escape analysis;
|
|
|
|
|
// because large values may contain pointers, it must happen early.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "escapes")
|
2016-03-11 14:28:16 -08:00
|
|
|
escapes(xtop)
|
|
|
|
|
|
|
|
|
|
// Phase 7: Transform closure bodies to properly reference captured variables.
|
|
|
|
|
// This needs to happen before walk, because closures must be transformed
|
|
|
|
|
// before walk reaches a call of a closure.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("fe", "xclosures")
|
2016-03-11 14:28:16 -08:00
|
|
|
for _, n := range xtop {
|
|
|
|
|
if n.Op == ODCLFUNC && n.Func.Closure != nil {
|
|
|
|
|
Curfn = n
|
|
|
|
|
transformclosure(n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Curfn = nil
|
|
|
|
|
|
|
|
|
|
// Phase 8: Compile top level functions.
|
|
|
|
|
// Don't use range--walk can add functions to xtop.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("be", "compilefuncs")
|
|
|
|
|
fcount = 0
|
2016-03-11 14:28:16 -08:00
|
|
|
for i := 0; i < len(xtop); i++ {
|
|
|
|
|
if xtop[i].Op == ODCLFUNC {
|
|
|
|
|
funccompile(xtop[i])
|
2016-06-24 15:03:04 -07:00
|
|
|
fcount++
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.AddEvent(fcount, "funcs")
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
if nsavederrors+nerrors == 0 {
|
|
|
|
|
fninit(xtop)
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-13 18:37:18 -07:00
|
|
|
if compiling_runtime {
|
2016-03-11 14:28:16 -08:00
|
|
|
checknowritebarrierrec()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Phase 9: Check external declarations.
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("be", "externaldcls")
|
2016-03-11 14:28:16 -08:00
|
|
|
for i, n := range externdcl {
|
|
|
|
|
if n.Op == ONAME {
|
cmd/compile: reduce use of **Node parameters
Escape analysis has a hard time with tree-like
structures (see #13493 and #14858).
This is unlikely to change.
As a result, when invoking a function that accepts
a **Node parameter, we usually allocate a *Node
on the heap. This happens a whole lot.
This CL changes functions from taking a **Node
to acting more like append: It both modifies
the input and returns a replacement for it.
Because of the cascading nature of escape analysis,
in order to get the benefits, I had to modify
almost all such functions. The remaining functions
are in racewalk and the backend. I would be happy
to update them as well in a separate CL.
This CL was created by manually updating the
function signatures and the directly impacted
bits of code. The callsites were then automatically
updated using a bespoke script:
https://gist.github.com/josharian/046b1be7aceae244de39
For ease of reviewing and future understanding,
this CL is also broken down into four CLs,
mailed separately, which show the manual
and the automated changes separately.
They are CLs 20990, 20991, 20992, and 20993.
Passes toolstash -cmp.
name old time/op new time/op delta
Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24)
Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24)
GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24)
Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24)
MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23)
name old alloc/op new alloc/op delta
Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23)
Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25)
GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25)
Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25)
name old allocs/op new allocs/op delta
Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25)
Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24)
GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25)
Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25)
name old text-bytes new text-bytes delta
HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal)
CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal)
name old data-bytes new data-bytes delta
HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal)
CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal)
name old exe-bytes new exe-bytes delta
HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal)
CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal)
Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475
Reviewed-on: https://go-review.googlesource.com/20959
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
|
|
|
externdcl[i] = typecheck(externdcl[i], Erv)
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nerrors+nsavederrors != 0 {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Start("be", "dumpobj")
|
2016-03-11 14:28:16 -08:00
|
|
|
dumpobj()
|
|
|
|
|
if asmhdr != "" {
|
|
|
|
|
dumpasmhdr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nerrors+nsavederrors != 0 {
|
|
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 15:45:10 +10:00
|
|
|
flusherrors()
|
2016-06-24 15:03:04 -07:00
|
|
|
timings.Stop()
|
|
|
|
|
|
|
|
|
|
if benchfile != "" {
|
|
|
|
|
if err := writebench(benchfile); err != nil {
|
|
|
|
|
log.Fatalf("cannot write benchmark data: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writebench(filename string) error {
|
|
|
|
|
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2016-09-09 08:13:16 -04:00
|
|
|
fmt.Fprintln(&buf, "commit:", obj.Version)
|
2016-06-24 15:03:04 -07:00
|
|
|
fmt.Fprintln(&buf, "goos:", runtime.GOOS)
|
|
|
|
|
fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
|
|
|
|
|
timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":")
|
|
|
|
|
|
|
|
|
|
n, err := f.Write(buf.Bytes())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if n != buf.Len() {
|
|
|
|
|
panic("bad writer")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return f.Close()
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var importMap = map[string]string{}
|
|
|
|
|
|
|
|
|
|
func addImportMap(s string) {
|
|
|
|
|
if strings.Count(s, "=") != 1 {
|
|
|
|
|
log.Fatal("-importmap argument must be of the form source=actual")
|
|
|
|
|
}
|
|
|
|
|
i := strings.Index(s, "=")
|
|
|
|
|
source, actual := s[:i], s[i+1:]
|
|
|
|
|
if source == "" || actual == "" {
|
|
|
|
|
log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
|
|
|
|
|
}
|
|
|
|
|
importMap[source] = actual
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func saveerrors() {
|
|
|
|
|
nsavederrors += nerrors
|
|
|
|
|
nerrors = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func arsize(b *bufio.Reader, name string) int {
|
|
|
|
|
var buf [ArhdrSize]byte
|
|
|
|
|
if _, err := io.ReadFull(b, buf[:]); err != nil {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
aname := strings.Trim(string(buf[0:16]), " ")
|
|
|
|
|
if !strings.HasPrefix(aname, name) {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
asize := strings.Trim(string(buf[48:58]), " ")
|
|
|
|
|
i, _ := strconv.Atoi(asize)
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func skiptopkgdef(b *bufio.Reader) bool {
|
|
|
|
|
// archive header
|
|
|
|
|
p, err := b.ReadString('\n')
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("reading input: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if p != "!<arch>\n" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// package export block should be first
|
|
|
|
|
sz := arsize(b, "__.PKGDEF")
|
|
|
|
|
return sz > 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var idirs []string
|
|
|
|
|
|
|
|
|
|
func addidir(dir string) {
|
|
|
|
|
if dir != "" {
|
|
|
|
|
idirs = append(idirs, dir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isDriveLetter(b byte) bool {
|
|
|
|
|
return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// is this path a local name? begins with ./ or ../ or /
|
|
|
|
|
func islocalname(name string) bool {
|
|
|
|
|
return strings.HasPrefix(name, "/") ||
|
2016-03-21 14:37:57 +11:00
|
|
|
runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
|
2016-03-11 14:28:16 -08:00
|
|
|
strings.HasPrefix(name, "./") || name == "." ||
|
|
|
|
|
strings.HasPrefix(name, "../") || name == ".."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findpkg(name string) (file string, ok bool) {
|
|
|
|
|
if islocalname(name) {
|
2016-04-13 18:37:18 -07:00
|
|
|
if safemode || nolocalimports {
|
2016-03-11 14:28:16 -08:00
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try .a before .6. important for building libraries:
|
|
|
|
|
// if there is an array.6 in the array.a library,
|
|
|
|
|
// want to find all of array.a, not just array.6.
|
|
|
|
|
file = fmt.Sprintf("%s.a", name)
|
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
|
|
|
|
file = fmt.Sprintf("%s.o", name)
|
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// local imports should be canonicalized already.
|
|
|
|
|
// don't want to see "encoding/../encoding/base64"
|
|
|
|
|
// as different from "encoding/base64".
|
|
|
|
|
if q := path.Clean(name); q != name {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("non-canonical import path %q (should be %q)", name, q)
|
2016-03-11 14:28:16 -08:00
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, dir := range idirs {
|
|
|
|
|
file = fmt.Sprintf("%s/%s.a", dir, name)
|
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
|
|
|
|
file = fmt.Sprintf("%s/%s.o", dir, name)
|
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 08:13:16 -04:00
|
|
|
if obj.GOROOT != "" {
|
2016-03-11 14:28:16 -08:00
|
|
|
suffix := ""
|
|
|
|
|
suffixsep := ""
|
|
|
|
|
if flag_installsuffix != "" {
|
|
|
|
|
suffixsep = "_"
|
|
|
|
|
suffix = flag_installsuffix
|
2016-04-13 18:37:18 -07:00
|
|
|
} else if flag_race {
|
2016-03-11 14:28:16 -08:00
|
|
|
suffixsep = "_"
|
|
|
|
|
suffix = "race"
|
2016-04-13 18:37:18 -07:00
|
|
|
} else if flag_msan {
|
2016-03-11 14:28:16 -08:00
|
|
|
suffixsep = "_"
|
|
|
|
|
suffix = "msan"
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 08:13:16 -04:00
|
|
|
file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", obj.GOROOT, obj.GOOS, obj.GOARCH, suffixsep, suffix, name)
|
2016-03-11 14:28:16 -08:00
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
2016-09-09 08:13:16 -04:00
|
|
|
file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", obj.GOROOT, obj.GOOS, obj.GOARCH, suffixsep, suffix, name)
|
2016-03-11 14:28:16 -08:00
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
|
return file, true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// loadsys loads the definitions for the low-level runtime and unsafe functions,
|
|
|
|
|
// so that the compiler can generate calls to them,
|
|
|
|
|
// but does not make the names "runtime" or "unsafe" visible as packages.
|
|
|
|
|
func loadsys() {
|
|
|
|
|
if Debug['A'] != 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = 1
|
|
|
|
|
iota_ = -1000000
|
|
|
|
|
|
2016-08-16 12:55:17 -07:00
|
|
|
importpkg = Runtimepkg
|
|
|
|
|
Import(bufio.NewReader(strings.NewReader(runtimeimport)))
|
|
|
|
|
importpkg = unsafepkg
|
|
|
|
|
Import(bufio.NewReader(strings.NewReader(unsafeimport)))
|
2016-03-11 14:28:16 -08:00
|
|
|
importpkg = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func importfile(f *Val, indent []byte) {
|
|
|
|
|
if importpkg != nil {
|
|
|
|
|
Fatalf("importpkg not nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path_, ok := f.U.(string)
|
|
|
|
|
if !ok {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import statement not a string")
|
2016-03-11 14:28:16 -08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(path_) == 0 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import path is empty")
|
2016-03-11 14:28:16 -08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isbadimport(path_) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The package name main is no longer reserved,
|
|
|
|
|
// but we reserve the import path "main" to identify
|
|
|
|
|
// the main package, just as we reserve the import
|
|
|
|
|
// path "math" to identify the standard math package.
|
|
|
|
|
if path_ == "main" {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("cannot import \"main\"")
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if myimportpath != "" && path_ == myimportpath {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import %q while compiling that package (import cycle)", path_)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if mapped, ok := importMap[path_]; ok {
|
|
|
|
|
path_ = mapped
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if path_ == "unsafe" {
|
2016-04-13 18:37:18 -07:00
|
|
|
if safemode {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("cannot import package unsafe")
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
importpkg = unsafepkg
|
|
|
|
|
imported_unsafe = true
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if islocalname(path_) {
|
|
|
|
|
if path_[0] == '/' {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import path cannot be absolute path")
|
2016-03-11 14:28:16 -08:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prefix := Ctxt.Pathname
|
|
|
|
|
if localimport != "" {
|
|
|
|
|
prefix = localimport
|
|
|
|
|
}
|
|
|
|
|
path_ = path.Join(prefix, path_)
|
|
|
|
|
|
|
|
|
|
if isbadimport(path_) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file, found := findpkg(path_)
|
|
|
|
|
if !found {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("can't find import: %q", path_)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
importpkg = mkpkg(path_)
|
|
|
|
|
|
|
|
|
|
if importpkg.Imported {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
importpkg.Imported = true
|
|
|
|
|
|
|
|
|
|
impf, err := os.Open(file)
|
|
|
|
|
if err != nil {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("can't open import: %q: %v", path_, err)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
defer impf.Close()
|
|
|
|
|
imp := bufio.NewReader(impf)
|
|
|
|
|
|
|
|
|
|
if strings.HasSuffix(file, ".a") {
|
|
|
|
|
if !skiptopkgdef(imp) {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import %s: not a package file", file)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check object header
|
|
|
|
|
p, err := imp.ReadString('\n')
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("reading input: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if len(p) > 0 {
|
|
|
|
|
p = p[:len(p)-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p != "empty archive" {
|
|
|
|
|
if !strings.HasPrefix(p, "go object ") {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import %s: not a go object file: %s", file, p)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-09 08:13:16 -04:00
|
|
|
q := fmt.Sprintf("%s %s %s %s", obj.GOOS, obj.GOARCH, obj.Version, obj.Expstring())
|
2016-03-11 14:28:16 -08:00
|
|
|
if p[10:] != q {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-20 11:19:19 -07:00
|
|
|
// process header lines
|
2016-09-13 15:33:55 -07:00
|
|
|
safe := false
|
2016-05-20 11:19:19 -07:00
|
|
|
for {
|
|
|
|
|
p, err = imp.ReadString('\n')
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("reading input: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if p == "\n" {
|
|
|
|
|
break // header ends with blank line
|
|
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(p, "safe") {
|
2016-09-13 15:33:55 -07:00
|
|
|
safe = true
|
2016-05-20 11:19:19 -07:00
|
|
|
break // ok to ignore rest
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-13 15:33:55 -07:00
|
|
|
if safemode && !safe {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("cannot import unsafe package %q", importpkg.Path)
|
2016-09-13 15:33:55 -07:00
|
|
|
}
|
2016-05-20 11:19:19 -07:00
|
|
|
|
2016-03-11 14:28:16 -08:00
|
|
|
// assume files move (get installed)
|
|
|
|
|
// so don't record the full path.
|
|
|
|
|
linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
|
|
|
|
|
|
|
|
|
|
// In the importfile, if we find:
|
2016-08-16 12:55:17 -07:00
|
|
|
// $$\n (textual format): not supported anymore
|
|
|
|
|
// $$B\n (binary format) : import directly, then feed the lexer a dummy statement
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
// look for $$
|
|
|
|
|
var c byte
|
|
|
|
|
for {
|
|
|
|
|
c, err = imp.ReadByte()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if c == '$' {
|
|
|
|
|
c, err = imp.ReadByte()
|
|
|
|
|
if c == '$' || err != nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get character after $$
|
|
|
|
|
if err == nil {
|
|
|
|
|
c, _ = imp.ReadByte()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch c {
|
|
|
|
|
case '\n':
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
|
2016-03-11 14:28:16 -08:00
|
|
|
|
|
|
|
|
case 'B':
|
2016-03-18 17:21:32 -07:00
|
|
|
if Debug_export != 0 {
|
|
|
|
|
fmt.Printf("importing %s (%s)\n", path_, file)
|
|
|
|
|
}
|
2016-03-11 14:28:16 -08:00
|
|
|
imp.ReadByte() // skip \n after $$B
|
|
|
|
|
Import(imp)
|
|
|
|
|
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("no import in %q", path_)
|
2016-03-11 14:28:16 -08:00
|
|
|
errorexit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func pkgnotused(lineno int32, path string, name string) {
|
|
|
|
|
// If the package was imported with a name other than the final
|
|
|
|
|
// import path element, show it explicitly in the error message.
|
|
|
|
|
// Note that this handles both renamed imports and imports of
|
|
|
|
|
// packages containing unconventional package declarations.
|
|
|
|
|
// Note that this uses / always, even on Windows, because Go import
|
|
|
|
|
// paths always use forward slashes.
|
|
|
|
|
elem := path
|
|
|
|
|
if i := strings.LastIndex(elem, "/"); i >= 0 {
|
|
|
|
|
elem = elem[i+1:]
|
|
|
|
|
}
|
|
|
|
|
if name == "" || elem == name {
|
|
|
|
|
yyerrorl(lineno, "imported and not used: %q", path)
|
|
|
|
|
} else {
|
|
|
|
|
yyerrorl(lineno, "imported and not used: %q as %s", path, name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mkpackage(pkgname string) {
|
|
|
|
|
if localpkg.Name == "" {
|
|
|
|
|
if pkgname == "_" {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("invalid package name _")
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
localpkg.Name = pkgname
|
|
|
|
|
} else {
|
|
|
|
|
if pkgname != localpkg.Name {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("package %s; expected %s", pkgname, localpkg.Name)
|
2016-03-11 14:28:16 -08:00
|
|
|
}
|
|
|
|
|
for _, s := range localpkg.Syms {
|
|
|
|
|
if s.Def == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if s.Def.Op == OPACK {
|
|
|
|
|
// throw away top-level package name leftover
|
|
|
|
|
// from previous file.
|
|
|
|
|
// leave s->block set to cause redeclaration
|
|
|
|
|
// errors if a conflicting top-level name is
|
|
|
|
|
// introduced by a different file.
|
|
|
|
|
if !s.Def.Used && nsyntaxerrors == 0 {
|
|
|
|
|
pkgnotused(s.Def.Lineno, s.Def.Name.Pkg.Path, s.Name)
|
|
|
|
|
}
|
|
|
|
|
s.Def = nil
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.Def.Sym != s {
|
|
|
|
|
// throw away top-level name left over
|
|
|
|
|
// from previous import . "x"
|
|
|
|
|
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
|
|
|
|
pkgnotused(s.Def.Name.Pack.Lineno, s.Def.Name.Pack.Name.Pkg.Path, "")
|
|
|
|
|
s.Def.Name.Pack.Used = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.Def = nil
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if outfile == "" {
|
|
|
|
|
p := infile
|
|
|
|
|
if i := strings.LastIndex(p, "/"); i >= 0 {
|
|
|
|
|
p = p[i+1:]
|
|
|
|
|
}
|
2016-03-21 14:37:57 +11:00
|
|
|
if runtime.GOOS == "windows" {
|
2016-03-11 14:28:16 -08:00
|
|
|
if i := strings.LastIndex(p, `\`); i >= 0 {
|
|
|
|
|
p = p[i+1:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if i := strings.LastIndex(p, "."); i >= 0 {
|
|
|
|
|
p = p[:i]
|
|
|
|
|
}
|
|
|
|
|
suffix := ".o"
|
2016-04-13 18:37:18 -07:00
|
|
|
if writearchive {
|
2016-03-11 14:28:16 -08:00
|
|
|
suffix = ".a"
|
|
|
|
|
}
|
|
|
|
|
outfile = p + suffix
|
|
|
|
|
}
|
|
|
|
|
}
|