github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/gc/main.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:generate go run mkbuiltin.go
     6  
     7  package gc
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"cmd/compile/internal/ssa"
    13  	"cmd/internal/obj"
    14  	"cmd/internal/sys"
    15  	"flag"
    16  	"fmt"
    17  	"io"
    18  	"log"
    19  	"os"
    20  	"path"
    21  	"runtime"
    22  	"strconv"
    23  	"strings"
    24  )
    25  
    26  var imported_unsafe bool
    27  
    28  var (
    29  	goos    string
    30  	goarch  string
    31  	goroot  string
    32  	buildid string
    33  
    34  	flag_newparser bool
    35  )
    36  
    37  var (
    38  	Debug_append  int
    39  	Debug_closure int
    40  	Debug_panic   int
    41  	Debug_slice   int
    42  	Debug_wb      int
    43  )
    44  
    45  // Debug arguments.
    46  // These can be specified with the -d flag, as in "-d nil"
    47  // to set the debug_checknil variable. In general the list passed
    48  // to -d can be comma-separated.
    49  var debugtab = []struct {
    50  	name string
    51  	val  *int
    52  }{
    53  	{"append", &Debug_append},         // print information about append compilation
    54  	{"closure", &Debug_closure},       // print information about closure compilation
    55  	{"disablenil", &Disable_checknil}, // disable nil checks
    56  	{"gcprog", &Debug_gcprog},         // print dump of GC programs
    57  	{"nil", &Debug_checknil},          // print information about nil checks
    58  	{"panic", &Debug_panic},           // do not hide any compiler panic
    59  	{"slice", &Debug_slice},           // print information about slice compilation
    60  	{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
    61  	{"wb", &Debug_wb},                 // print information about write barriers
    62  	{"export", &Debug_export},         // print export data
    63  }
    64  
    65  func usage() {
    66  	fmt.Printf("usage: compile [options] file.go...\n")
    67  	obj.Flagprint(1)
    68  	Exit(2)
    69  }
    70  
    71  func hidePanic() {
    72  	if Debug_panic == 0 && nsavederrors+nerrors > 0 {
    73  		// If we've already complained about things
    74  		// in the program, don't bother complaining
    75  		// about a panic too; let the user clean up
    76  		// the code and try again.
    77  		if err := recover(); err != nil {
    78  			errorexit()
    79  		}
    80  	}
    81  }
    82  
    83  func doversion() {
    84  	p := obj.Expstring()
    85  	if p == "X:none" {
    86  		p = ""
    87  	}
    88  	sep := ""
    89  	if p != "" {
    90  		sep = " "
    91  	}
    92  	fmt.Printf("compile version %s%s%s\n", obj.Getgoversion(), sep, p)
    93  	os.Exit(0)
    94  }
    95  
    96  // supportsDynlink reports whether or not the code generator for the given
    97  // architecture supports the -shared and -dynlink flags.
    98  func supportsDynlink(arch *sys.Arch) bool {
    99  	return arch.InFamily(sys.AMD64, sys.ARM, sys.ARM64, sys.I386, sys.PPC64, sys.S390X)
   100  }
   101  
   102  // timing data for compiler phases
   103  var timings Timings
   104  var benchfile string
   105  
   106  func Main() {
   107  	timings.Start("fe", "init")
   108  
   109  	defer hidePanic()
   110  
   111  	goarch = obj.Getgoarch()
   112  
   113  	Ctxt = obj.Linknew(Thearch.LinkArch)
   114  	Ctxt.DiagFunc = Yyerror
   115  	bstdout = bufio.NewWriter(os.Stdout)
   116  	Ctxt.Bso = bstdout
   117  
   118  	localpkg = mkpkg("")
   119  	localpkg.Prefix = "\"\""
   120  
   121  	// pseudo-package, for scoping
   122  	builtinpkg = mkpkg("go.builtin")
   123  	builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin
   124  
   125  	// pseudo-package, accessed by import "unsafe"
   126  	unsafepkg = mkpkg("unsafe")
   127  	unsafepkg.Name = "unsafe"
   128  
   129  	// real package, referred to by generated runtime calls
   130  	Runtimepkg = mkpkg("runtime")
   131  	Runtimepkg.Name = "runtime"
   132  
   133  	// pseudo-packages used in symbol tables
   134  	itabpkg = mkpkg("go.itab")
   135  	itabpkg.Name = "go.itab"
   136  	itabpkg.Prefix = "go.itab" // not go%2eitab
   137  
   138  	itablinkpkg = mkpkg("go.itablink")
   139  	itablinkpkg.Name = "go.itablink"
   140  	itablinkpkg.Prefix = "go.itablink" // not go%2eitablink
   141  
   142  	trackpkg = mkpkg("go.track")
   143  	trackpkg.Name = "go.track"
   144  	trackpkg.Prefix = "go.track" // not go%2etrack
   145  
   146  	typepkg = mkpkg("type")
   147  	typepkg.Name = "type"
   148  
   149  	// pseudo-package used for map zero values
   150  	mappkg = mkpkg("go.map")
   151  	mappkg.Name = "go.map"
   152  	mappkg.Prefix = "go.map"
   153  
   154  	goroot = obj.Getgoroot()
   155  	goos = obj.Getgoos()
   156  
   157  	Nacl = goos == "nacl"
   158  	if Nacl {
   159  		flag_largemodel = true
   160  	}
   161  
   162  	flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
   163  	obj.Flagcount("%", "debug non-static initializers", &Debug['%'])
   164  	obj.Flagcount("A", "for bootstrapping, allow 'any' type", &Debug['A'])
   165  	obj.Flagcount("B", "disable bounds checking", &Debug['B'])
   166  	flag.StringVar(&localimport, "D", "", "set relative `path` for local imports")
   167  	obj.Flagcount("E", "debug symbol export", &Debug['E'])
   168  	obj.Flagfn1("I", "add `directory` to import search path", addidir)
   169  	obj.Flagcount("K", "debug missing line numbers", &Debug['K'])
   170  	obj.Flagcount("M", "debug move generation", &Debug['M'])
   171  	obj.Flagcount("N", "disable optimizations", &Debug['N'])
   172  	obj.Flagcount("P", "debug peephole optimizer", &Debug['P'])
   173  	obj.Flagcount("R", "debug register optimizer", &Debug['R'])
   174  	obj.Flagcount("S", "print assembly listing", &Debug['S'])
   175  	obj.Flagfn0("V", "print compiler version", doversion)
   176  	obj.Flagcount("W", "debug parse tree after type checking", &Debug['W'])
   177  	flag.StringVar(&asmhdr, "asmhdr", "", "write assembly header to `file`")
   178  	flag.StringVar(&buildid, "buildid", "", "record `id` as the build id in the export metadata")
   179  	flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
   180  	flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`")
   181  	obj.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
   182  	obj.Flagcount("f", "debug stack frames", &Debug['f'])
   183  	obj.Flagcount("g", "debug code generation", &Debug['g'])
   184  	obj.Flagcount("h", "halt on error", &Debug['h'])
   185  	obj.Flagcount("i", "debug line number stack", &Debug['i'])
   186  	obj.Flagfn1("importmap", "add `definition` of the form source=actual to import map", addImportMap)
   187  	flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`")
   188  	obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j'])
   189  	obj.Flagcount("l", "disable inlining", &Debug['l'])
   190  	flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`")
   191  	obj.Flagcount("live", "debug liveness analysis", &debuglive)
   192  	obj.Flagcount("m", "print optimization decisions", &Debug['m'])
   193  	flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer")
   194  	flag.BoolVar(&flag_newparser, "newparser", false, "use new parser")
   195  	flag.BoolVar(&nolocalimports, "nolocalimports", false, "reject local (relative) imports")
   196  	flag.StringVar(&outfile, "o", "", "write output to `file`")
   197  	flag.StringVar(&myimportpath, "p", "", "set expected package import `path`")
   198  	flag.BoolVar(&writearchive, "pack", false, "write package file instead of object file")
   199  	obj.Flagcount("r", "debug generated wrappers", &Debug['r'])
   200  	flag.BoolVar(&flag_race, "race", false, "enable race detector")
   201  	obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s'])
   202  	flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths")
   203  	flag.BoolVar(&safemode, "u", false, "reject unsafe code")
   204  	obj.Flagcount("v", "increase debug verbosity", &Debug['v'])
   205  	obj.Flagcount("w", "debug type checking", &Debug['w'])
   206  	flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier")
   207  	obj.Flagcount("x", "debug lexer", &Debug['x'])
   208  	var flag_shared bool
   209  	var flag_dynlink bool
   210  	if supportsDynlink(Thearch.LinkArch.Arch) {
   211  		flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
   212  		flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
   213  	}
   214  	if Thearch.LinkArch.Family == sys.AMD64 {
   215  		flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
   216  	}
   217  	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
   218  	flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
   219  	flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
   220  	flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code")
   221  	flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
   222  	obj.Flagparse(usage)
   223  
   224  	Ctxt.Flag_shared = flag_dynlink || flag_shared
   225  	Ctxt.Flag_dynlink = flag_dynlink
   226  	Ctxt.Flag_optimize = Debug['N'] == 0
   227  
   228  	Ctxt.Debugasm = int32(Debug['S'])
   229  	Ctxt.Debugvlog = int32(Debug['v'])
   230  
   231  	if flag.NArg() < 1 {
   232  		usage()
   233  	}
   234  
   235  	startProfile()
   236  
   237  	if flag_race {
   238  		racepkg = mkpkg("runtime/race")
   239  		racepkg.Name = "race"
   240  	}
   241  	if flag_msan {
   242  		msanpkg = mkpkg("runtime/msan")
   243  		msanpkg.Name = "msan"
   244  	}
   245  	if flag_race && flag_msan {
   246  		log.Fatal("cannot use both -race and -msan")
   247  	} else if flag_race || flag_msan {
   248  		instrumenting = true
   249  	}
   250  
   251  	// parse -d argument
   252  	if debugstr != "" {
   253  	Split:
   254  		for _, name := range strings.Split(debugstr, ",") {
   255  			if name == "" {
   256  				continue
   257  			}
   258  			val := 1
   259  			if i := strings.Index(name, "="); i >= 0 {
   260  				var err error
   261  				val, err = strconv.Atoi(name[i+1:])
   262  				if err != nil {
   263  					log.Fatalf("invalid debug value %v", name)
   264  				}
   265  				name = name[:i]
   266  			}
   267  			for _, t := range debugtab {
   268  				if t.name == name {
   269  					if t.val != nil {
   270  						*t.val = val
   271  						continue Split
   272  					}
   273  				}
   274  			}
   275  			// special case for ssa for now
   276  			if strings.HasPrefix(name, "ssa/") {
   277  				// expect form ssa/phase/flag
   278  				// e.g. -d=ssa/generic_cse/time
   279  				// _ in phase name also matches space
   280  				phase := name[4:]
   281  				flag := "debug" // default flag is debug
   282  				if i := strings.Index(phase, "/"); i >= 0 {
   283  					flag = phase[i+1:]
   284  					phase = phase[:i]
   285  				}
   286  				err := ssa.PhaseOption(phase, flag, val)
   287  				if err != "" {
   288  					log.Fatalf(err)
   289  				}
   290  				continue Split
   291  			}
   292  			log.Fatalf("unknown debug key -d %s\n", name)
   293  		}
   294  	}
   295  
   296  	// enable inlining.  for now:
   297  	//	default: inlining on.  (debug['l'] == 1)
   298  	//	-l: inlining off  (debug['l'] == 0)
   299  	//	-ll, -lll: inlining on again, with extra debugging (debug['l'] > 1)
   300  	if Debug['l'] <= 1 {
   301  		Debug['l'] = 1 - Debug['l']
   302  	}
   303  
   304  	Thearch.Betypeinit()
   305  	Widthint = Thearch.LinkArch.IntSize
   306  	Widthptr = Thearch.LinkArch.PtrSize
   307  	Widthreg = Thearch.LinkArch.RegSize
   308  
   309  	initUniverse()
   310  
   311  	blockgen = 1
   312  	dclcontext = PEXTERN
   313  	nerrors = 0
   314  	lexlineno = 1
   315  
   316  	timings.Start("fe", "loadsys")
   317  	loadsys()
   318  
   319  	timings.Start("fe", "parse")
   320  	lexlineno0 := lexlineno
   321  	for _, infile = range flag.Args() {
   322  		if trace && Debug['x'] != 0 {
   323  			fmt.Printf("--- %s ---\n", infile)
   324  		}
   325  
   326  		linehistpush(infile)
   327  		block = 1
   328  		iota_ = -1000000
   329  		imported_unsafe = false
   330  		if flag_newparser {
   331  			parseFile(infile)
   332  		} else {
   333  			oldParseFile(infile)
   334  		}
   335  		if nsyntaxerrors != 0 {
   336  			errorexit()
   337  		}
   338  
   339  		// Instead of converting EOF into '\n' in getc and count it as an extra line
   340  		// for the line history to work, and which then has to be corrected elsewhere,
   341  		// just add a line here.
   342  		lexlineno++
   343  		linehistpop()
   344  	}
   345  	timings.Stop()
   346  	timings.AddEvent(int64(lexlineno-lexlineno0), "lines")
   347  
   348  	testdclstack()
   349  	mkpackage(localpkg.Name) // final import not used checks
   350  	finishUniverse()
   351  
   352  	typecheckok = true
   353  	if Debug['f'] != 0 {
   354  		frame(1)
   355  	}
   356  
   357  	// Process top-level declarations in phases.
   358  
   359  	// Phase 1: const, type, and names and types of funcs.
   360  	//   This will gather all the information about types
   361  	//   and methods but doesn't depend on any of it.
   362  	defercheckwidth()
   363  
   364  	// Don't use range--typecheck can add closures to xtop.
   365  	timings.Start("fe", "typecheck", "top1")
   366  	for i := 0; i < len(xtop); i++ {
   367  		if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
   368  			xtop[i] = typecheck(xtop[i], Etop)
   369  		}
   370  	}
   371  
   372  	// Phase 2: Variable assignments.
   373  	//   To check interface assignments, depends on phase 1.
   374  
   375  	// Don't use range--typecheck can add closures to xtop.
   376  	timings.Start("fe", "typecheck", "top2")
   377  	for i := 0; i < len(xtop); i++ {
   378  		if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
   379  			xtop[i] = typecheck(xtop[i], Etop)
   380  		}
   381  	}
   382  	resumecheckwidth()
   383  
   384  	// Phase 3: Type check function bodies.
   385  	// Don't use range--typecheck can add closures to xtop.
   386  	timings.Start("fe", "typecheck", "func")
   387  	var fcount int64
   388  	for i := 0; i < len(xtop); i++ {
   389  		if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
   390  			Curfn = xtop[i]
   391  			decldepth = 1
   392  			saveerrors()
   393  			typecheckslice(Curfn.Nbody.Slice(), Etop)
   394  			checkreturn(Curfn)
   395  			if nerrors != 0 {
   396  				Curfn.Nbody.Set(nil) // type errors; do not compile
   397  			}
   398  			fcount++
   399  		}
   400  	}
   401  	timings.AddEvent(fcount, "funcs")
   402  
   403  	// Phase 4: Decide how to capture closed variables.
   404  	// This needs to run before escape analysis,
   405  	// because variables captured by value do not escape.
   406  	timings.Start("fe", "capturevars")
   407  	for _, n := range xtop {
   408  		if n.Op == ODCLFUNC && n.Func.Closure != nil {
   409  			Curfn = n
   410  			capturevars(n)
   411  		}
   412  	}
   413  
   414  	Curfn = nil
   415  
   416  	if nsavederrors+nerrors != 0 {
   417  		errorexit()
   418  	}
   419  
   420  	// Phase 5: Inlining
   421  	timings.Start("fe", "inlining")
   422  	if Debug['l'] > 1 {
   423  		// Typecheck imported function bodies if debug['l'] > 1,
   424  		// otherwise lazily when used or re-exported.
   425  		for _, n := range importlist {
   426  			if n.Func.Inl.Len() != 0 {
   427  				saveerrors()
   428  				typecheckinl(n)
   429  			}
   430  		}
   431  
   432  		if nsavederrors+nerrors != 0 {
   433  			errorexit()
   434  		}
   435  	}
   436  
   437  	if Debug['l'] != 0 {
   438  		// Find functions that can be inlined and clone them before walk expands them.
   439  		visitBottomUp(xtop, func(list []*Node, recursive bool) {
   440  			for _, n := range list {
   441  				if !recursive {
   442  					caninl(n)
   443  				} else {
   444  					if Debug['m'] > 1 {
   445  						fmt.Printf("%v: cannot inline %v: recursive\n", n.Line(), n.Func.Nname)
   446  					}
   447  				}
   448  				inlcalls(n)
   449  			}
   450  		})
   451  	}
   452  
   453  	// Phase 6: Escape analysis.
   454  	// Required for moving heap allocations onto stack,
   455  	// which in turn is required by the closure implementation,
   456  	// which stores the addresses of stack variables into the closure.
   457  	// If the closure does not escape, it needs to be on the stack
   458  	// or else the stack copier will not update it.
   459  	// Large values are also moved off stack in escape analysis;
   460  	// because large values may contain pointers, it must happen early.
   461  	timings.Start("fe", "escapes")
   462  	escapes(xtop)
   463  
   464  	// Phase 7: Transform closure bodies to properly reference captured variables.
   465  	// This needs to happen before walk, because closures must be transformed
   466  	// before walk reaches a call of a closure.
   467  	timings.Start("fe", "xclosures")
   468  	for _, n := range xtop {
   469  		if n.Op == ODCLFUNC && n.Func.Closure != nil {
   470  			Curfn = n
   471  			transformclosure(n)
   472  		}
   473  	}
   474  
   475  	Curfn = nil
   476  
   477  	// Phase 8: Compile top level functions.
   478  	// Don't use range--walk can add functions to xtop.
   479  	timings.Start("be", "compilefuncs")
   480  	fcount = 0
   481  	for i := 0; i < len(xtop); i++ {
   482  		if xtop[i].Op == ODCLFUNC {
   483  			funccompile(xtop[i])
   484  			fcount++
   485  		}
   486  	}
   487  	timings.AddEvent(fcount, "funcs")
   488  
   489  	if nsavederrors+nerrors == 0 {
   490  		fninit(xtop)
   491  	}
   492  
   493  	if compiling_runtime {
   494  		checknowritebarrierrec()
   495  	}
   496  
   497  	// Phase 9: Check external declarations.
   498  	timings.Start("be", "externaldcls")
   499  	for i, n := range externdcl {
   500  		if n.Op == ONAME {
   501  			externdcl[i] = typecheck(externdcl[i], Erv)
   502  		}
   503  	}
   504  
   505  	if nerrors+nsavederrors != 0 {
   506  		errorexit()
   507  	}
   508  
   509  	timings.Start("be", "dumpobj")
   510  	dumpobj()
   511  	if asmhdr != "" {
   512  		dumpasmhdr()
   513  	}
   514  
   515  	if nerrors+nsavederrors != 0 {
   516  		errorexit()
   517  	}
   518  
   519  	Flusherrors()
   520  	timings.Stop()
   521  
   522  	if benchfile != "" {
   523  		if err := writebench(benchfile); err != nil {
   524  			log.Fatalf("cannot write benchmark data: %v", err)
   525  		}
   526  	}
   527  }
   528  
   529  func writebench(filename string) error {
   530  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
   531  	if err != nil {
   532  		return err
   533  	}
   534  
   535  	var buf bytes.Buffer
   536  	fmt.Fprintln(&buf, "commit:", obj.Getgoversion())
   537  	fmt.Fprintln(&buf, "goos:", runtime.GOOS)
   538  	fmt.Fprintln(&buf, "goarch:", runtime.GOARCH)
   539  	timings.Write(&buf, "BenchmarkCompile:"+myimportpath+":")
   540  
   541  	n, err := f.Write(buf.Bytes())
   542  	if err != nil {
   543  		return err
   544  	}
   545  	if n != buf.Len() {
   546  		panic("bad writer")
   547  	}
   548  
   549  	return f.Close()
   550  }
   551  
   552  var importMap = map[string]string{}
   553  
   554  func addImportMap(s string) {
   555  	if strings.Count(s, "=") != 1 {
   556  		log.Fatal("-importmap argument must be of the form source=actual")
   557  	}
   558  	i := strings.Index(s, "=")
   559  	source, actual := s[:i], s[i+1:]
   560  	if source == "" || actual == "" {
   561  		log.Fatal("-importmap argument must be of the form source=actual; source and actual must be non-empty")
   562  	}
   563  	importMap[source] = actual
   564  }
   565  
   566  func saveerrors() {
   567  	nsavederrors += nerrors
   568  	nerrors = 0
   569  }
   570  
   571  func arsize(b *bufio.Reader, name string) int {
   572  	var buf [ArhdrSize]byte
   573  	if _, err := io.ReadFull(b, buf[:]); err != nil {
   574  		return -1
   575  	}
   576  	aname := strings.Trim(string(buf[0:16]), " ")
   577  	if !strings.HasPrefix(aname, name) {
   578  		return -1
   579  	}
   580  	asize := strings.Trim(string(buf[48:58]), " ")
   581  	i, _ := strconv.Atoi(asize)
   582  	return i
   583  }
   584  
   585  func skiptopkgdef(b *bufio.Reader) bool {
   586  	// archive header
   587  	p, err := b.ReadString('\n')
   588  	if err != nil {
   589  		log.Fatalf("reading input: %v", err)
   590  	}
   591  	if p != "!<arch>\n" {
   592  		return false
   593  	}
   594  
   595  	// package export block should be first
   596  	sz := arsize(b, "__.PKGDEF")
   597  	return sz > 0
   598  }
   599  
   600  var idirs []string
   601  
   602  func addidir(dir string) {
   603  	if dir != "" {
   604  		idirs = append(idirs, dir)
   605  	}
   606  }
   607  
   608  func isDriveLetter(b byte) bool {
   609  	return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
   610  }
   611  
   612  // is this path a local name?  begins with ./ or ../ or /
   613  func islocalname(name string) bool {
   614  	return strings.HasPrefix(name, "/") ||
   615  		runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
   616  		strings.HasPrefix(name, "./") || name == "." ||
   617  		strings.HasPrefix(name, "../") || name == ".."
   618  }
   619  
   620  func findpkg(name string) (file string, ok bool) {
   621  	if islocalname(name) {
   622  		if safemode || nolocalimports {
   623  			return "", false
   624  		}
   625  
   626  		// try .a before .6.  important for building libraries:
   627  		// if there is an array.6 in the array.a library,
   628  		// want to find all of array.a, not just array.6.
   629  		file = fmt.Sprintf("%s.a", name)
   630  		if _, err := os.Stat(file); err == nil {
   631  			return file, true
   632  		}
   633  		file = fmt.Sprintf("%s.o", name)
   634  		if _, err := os.Stat(file); err == nil {
   635  			return file, true
   636  		}
   637  		return "", false
   638  	}
   639  
   640  	// local imports should be canonicalized already.
   641  	// don't want to see "encoding/../encoding/base64"
   642  	// as different from "encoding/base64".
   643  	if q := path.Clean(name); q != name {
   644  		Yyerror("non-canonical import path %q (should be %q)", name, q)
   645  		return "", false
   646  	}
   647  
   648  	for _, dir := range idirs {
   649  		file = fmt.Sprintf("%s/%s.a", dir, name)
   650  		if _, err := os.Stat(file); err == nil {
   651  			return file, true
   652  		}
   653  		file = fmt.Sprintf("%s/%s.o", dir, name)
   654  		if _, err := os.Stat(file); err == nil {
   655  			return file, true
   656  		}
   657  	}
   658  
   659  	if goroot != "" {
   660  		suffix := ""
   661  		suffixsep := ""
   662  		if flag_installsuffix != "" {
   663  			suffixsep = "_"
   664  			suffix = flag_installsuffix
   665  		} else if flag_race {
   666  			suffixsep = "_"
   667  			suffix = "race"
   668  		} else if flag_msan {
   669  			suffixsep = "_"
   670  			suffix = "msan"
   671  		}
   672  
   673  		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name)
   674  		if _, err := os.Stat(file); err == nil {
   675  			return file, true
   676  		}
   677  		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
   678  		if _, err := os.Stat(file); err == nil {
   679  			return file, true
   680  		}
   681  	}
   682  
   683  	return "", false
   684  }
   685  
   686  // loadsys loads the definitions for the low-level runtime and unsafe functions,
   687  // so that the compiler can generate calls to them,
   688  // but does not make the names "runtime" or "unsafe" visible as packages.
   689  func loadsys() {
   690  	if Debug['A'] != 0 {
   691  		return
   692  	}
   693  
   694  	block = 1
   695  	iota_ = -1000000
   696  	incannedimport = 1
   697  
   698  	importpkg = Runtimepkg
   699  	Import(bufio.NewReader(strings.NewReader(runtimeimport)))
   700  	importpkg = unsafepkg
   701  	Import(bufio.NewReader(strings.NewReader(unsafeimport)))
   702  
   703  	importpkg = nil
   704  	incannedimport = 0
   705  }
   706  
   707  func importfile(f *Val, indent []byte) {
   708  	if importpkg != nil {
   709  		Fatalf("importpkg not nil")
   710  	}
   711  
   712  	path_, ok := f.U.(string)
   713  	if !ok {
   714  		Yyerror("import statement not a string")
   715  		return
   716  	}
   717  
   718  	if len(path_) == 0 {
   719  		Yyerror("import path is empty")
   720  		return
   721  	}
   722  
   723  	if isbadimport(path_) {
   724  		return
   725  	}
   726  
   727  	// The package name main is no longer reserved,
   728  	// but we reserve the import path "main" to identify
   729  	// the main package, just as we reserve the import
   730  	// path "math" to identify the standard math package.
   731  	if path_ == "main" {
   732  		Yyerror("cannot import \"main\"")
   733  		errorexit()
   734  	}
   735  
   736  	if myimportpath != "" && path_ == myimportpath {
   737  		Yyerror("import %q while compiling that package (import cycle)", path_)
   738  		errorexit()
   739  	}
   740  
   741  	if mapped, ok := importMap[path_]; ok {
   742  		path_ = mapped
   743  	}
   744  
   745  	if path_ == "unsafe" {
   746  		if safemode {
   747  			Yyerror("cannot import package unsafe")
   748  			errorexit()
   749  		}
   750  
   751  		importpkg = unsafepkg
   752  		imported_unsafe = true
   753  		return
   754  	}
   755  
   756  	if islocalname(path_) {
   757  		if path_[0] == '/' {
   758  			Yyerror("import path cannot be absolute path")
   759  			return
   760  		}
   761  
   762  		prefix := Ctxt.Pathname
   763  		if localimport != "" {
   764  			prefix = localimport
   765  		}
   766  		path_ = path.Join(prefix, path_)
   767  
   768  		if isbadimport(path_) {
   769  			return
   770  		}
   771  	}
   772  
   773  	file, found := findpkg(path_)
   774  	if !found {
   775  		Yyerror("can't find import: %q", path_)
   776  		errorexit()
   777  	}
   778  
   779  	importpkg = mkpkg(path_)
   780  
   781  	if importpkg.Imported {
   782  		return
   783  	}
   784  
   785  	importpkg.Imported = true
   786  
   787  	impf, err := os.Open(file)
   788  	if err != nil {
   789  		Yyerror("can't open import: %q: %v", path_, err)
   790  		errorexit()
   791  	}
   792  	defer impf.Close()
   793  	imp := bufio.NewReader(impf)
   794  
   795  	if strings.HasSuffix(file, ".a") {
   796  		if !skiptopkgdef(imp) {
   797  			Yyerror("import %s: not a package file", file)
   798  			errorexit()
   799  		}
   800  	}
   801  
   802  	// check object header
   803  	p, err := imp.ReadString('\n')
   804  	if err != nil {
   805  		log.Fatalf("reading input: %v", err)
   806  	}
   807  	if len(p) > 0 {
   808  		p = p[:len(p)-1]
   809  	}
   810  
   811  	if p != "empty archive" {
   812  		if !strings.HasPrefix(p, "go object ") {
   813  			Yyerror("import %s: not a go object file: %s", file, p)
   814  			errorexit()
   815  		}
   816  
   817  		q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
   818  		if p[10:] != q {
   819  			Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q)
   820  			errorexit()
   821  		}
   822  	}
   823  
   824  	// process header lines
   825  	for {
   826  		p, err = imp.ReadString('\n')
   827  		if err != nil {
   828  			log.Fatalf("reading input: %v", err)
   829  		}
   830  		if p == "\n" {
   831  			break // header ends with blank line
   832  		}
   833  		if strings.HasPrefix(p, "safe") {
   834  			importpkg.Safe = true
   835  			break // ok to ignore rest
   836  		}
   837  	}
   838  
   839  	// assume files move (get installed)
   840  	// so don't record the full path.
   841  	linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
   842  
   843  	// In the importfile, if we find:
   844  	// $$\n  (textual format): not supported anymore
   845  	// $$B\n (binary format) : import directly, then feed the lexer a dummy statement
   846  
   847  	// look for $$
   848  	var c byte
   849  	for {
   850  		c, err = imp.ReadByte()
   851  		if err != nil {
   852  			break
   853  		}
   854  		if c == '$' {
   855  			c, err = imp.ReadByte()
   856  			if c == '$' || err != nil {
   857  				break
   858  			}
   859  		}
   860  	}
   861  
   862  	// get character after $$
   863  	if err == nil {
   864  		c, _ = imp.ReadByte()
   865  	}
   866  
   867  	switch c {
   868  	case '\n':
   869  		Yyerror("cannot import %s: old export format no longer supported (recompile library)", path_)
   870  
   871  	case 'B':
   872  		if Debug_export != 0 {
   873  			fmt.Printf("importing %s (%s)\n", path_, file)
   874  		}
   875  		imp.ReadByte() // skip \n after $$B
   876  		Import(imp)
   877  
   878  	default:
   879  		Yyerror("no import in %q", path_)
   880  		errorexit()
   881  	}
   882  
   883  	if safemode && !importpkg.Safe {
   884  		Yyerror("cannot import unsafe package %q", importpkg.Path)
   885  	}
   886  }
   887  
   888  func pkgnotused(lineno int32, path string, name string) {
   889  	// If the package was imported with a name other than the final
   890  	// import path element, show it explicitly in the error message.
   891  	// Note that this handles both renamed imports and imports of
   892  	// packages containing unconventional package declarations.
   893  	// Note that this uses / always, even on Windows, because Go import
   894  	// paths always use forward slashes.
   895  	elem := path
   896  	if i := strings.LastIndex(elem, "/"); i >= 0 {
   897  		elem = elem[i+1:]
   898  	}
   899  	if name == "" || elem == name {
   900  		yyerrorl(lineno, "imported and not used: %q", path)
   901  	} else {
   902  		yyerrorl(lineno, "imported and not used: %q as %s", path, name)
   903  	}
   904  }
   905  
   906  func mkpackage(pkgname string) {
   907  	if localpkg.Name == "" {
   908  		if pkgname == "_" {
   909  			Yyerror("invalid package name _")
   910  		}
   911  		localpkg.Name = pkgname
   912  	} else {
   913  		if pkgname != localpkg.Name {
   914  			Yyerror("package %s; expected %s", pkgname, localpkg.Name)
   915  		}
   916  		for _, s := range localpkg.Syms {
   917  			if s.Def == nil {
   918  				continue
   919  			}
   920  			if s.Def.Op == OPACK {
   921  				// throw away top-level package name leftover
   922  				// from previous file.
   923  				// leave s->block set to cause redeclaration
   924  				// errors if a conflicting top-level name is
   925  				// introduced by a different file.
   926  				if !s.Def.Used && nsyntaxerrors == 0 {
   927  					pkgnotused(s.Def.Lineno, s.Def.Name.Pkg.Path, s.Name)
   928  				}
   929  				s.Def = nil
   930  				continue
   931  			}
   932  
   933  			if s.Def.Sym != s {
   934  				// throw away top-level name left over
   935  				// from previous import . "x"
   936  				if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
   937  					pkgnotused(s.Def.Name.Pack.Lineno, s.Def.Name.Pack.Name.Pkg.Path, "")
   938  					s.Def.Name.Pack.Used = true
   939  				}
   940  
   941  				s.Def = nil
   942  				continue
   943  			}
   944  		}
   945  	}
   946  
   947  	if outfile == "" {
   948  		p := infile
   949  		if i := strings.LastIndex(p, "/"); i >= 0 {
   950  			p = p[i+1:]
   951  		}
   952  		if runtime.GOOS == "windows" {
   953  			if i := strings.LastIndex(p, `\`); i >= 0 {
   954  				p = p[i+1:]
   955  			}
   956  		}
   957  		if i := strings.LastIndex(p, "."); i >= 0 {
   958  			p = p[:i]
   959  		}
   960  		suffix := ".o"
   961  		if writearchive {
   962  			suffix = ".a"
   963  		}
   964  		outfile = p + suffix
   965  	}
   966  }