diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index facda14c37..23eb97a5e3 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -2,849 +2,24 @@ // 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" - "cmd/compile/internal/ssa" "cmd/internal/obj" - "flag" "fmt" "io" - "log" - "os" - "path" "strconv" "strings" "unicode" "unicode/utf8" ) -var imported_unsafe bool - -var ( - goos string - goarch string - goroot string - buildid string -) - -var ( - Debug_append int - Debug_panic int - Debug_slice int - Debug_wb int -) - -const BOM = 0xFEFF - -// 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 - {"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 -} - const ( EOF = -1 + BOM = 0xFEFF ) -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 = " " - } - fmt.Printf("compile version %s%s%s\n", obj.Getgoversion(), sep, p) - os.Exit(0) -} - -func Main() { - defer hidePanic() - - // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, - // but not other values. - p := obj.Getgoarch() - - if !strings.HasPrefix(p, Thearch.Thestring) { - log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) - } - goarch = p - - Thearch.Linkarchinit() - Ctxt = obj.Linknew(Thearch.Thelinkarch) - Ctxt.DiagFunc = Yyerror - Ctxt.Bso = &bstdout - bstdout = *obj.Binitw(os.Stdout) - - 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 - gostringpkg = mkpkg("go.string") - - gostringpkg.Name = "go.string" - gostringpkg.Prefix = "go.string" // not go%2estring - - itabpkg = mkpkg("go.itab") - - itabpkg.Name = "go.itab" - itabpkg.Prefix = "go.itab" // not go%2eitab - - typelinkpkg = mkpkg("go.typelink") - typelinkpkg.Name = "go.typelink" - typelinkpkg.Prefix = "go.typelink" // not go%2etypelink - - trackpkg = mkpkg("go.track") - - trackpkg.Name = "go.track" - trackpkg.Prefix = "go.track" // not go%2etrack - - typepkg = mkpkg("type") - - typepkg.Name = "type" - - goroot = obj.Getgoroot() - goos = obj.Getgoos() - - Nacl = goos == "nacl" - if Nacl { - flag_largemodel = 1 - } - - outfile = "" - obj.Flagcount("+", "compiling runtime", &compiling_runtime) - 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']) - obj.Flagstr("D", "set relative `path` for local imports", &localimport) - 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("L", "use full (long) path in error messages", &Debug['L']) - 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']) - obj.Flagstr("asmhdr", "write assembly header to `file`", &asmhdr) - obj.Flagstr("buildid", "record `id` as the build id in the export metadata", &buildid) - obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) - obj.Flagstr("d", "print debug information about items in `list`", &debugstr) - 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) - obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix) - obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) - obj.Flagcount("l", "disable inlining", &Debug['l']) - obj.Flagcount("live", "debug liveness analysis", &debuglive) - obj.Flagcount("m", "print optimization decisions", &Debug['m']) - obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan) - obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually (issue 13241) - obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) - obj.Flagstr("o", "write output to `file`", &outfile) - obj.Flagstr("p", "set expected package import `path`", &myimportpath) - obj.Flagcount("pack", "write package file instead of object file", &writearchive) - obj.Flagcount("r", "debug generated wrappers", &Debug['r']) - obj.Flagcount("race", "enable race detector", &flag_race) - obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) - obj.Flagstr("trimpath", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix) - obj.Flagcount("u", "reject unsafe code", &safemode) - obj.Flagcount("v", "increase debug verbosity", &Debug['v']) - obj.Flagcount("w", "debug type checking", &Debug['w']) - use_writebarrier = 1 - obj.Flagcount("wb", "enable write barrier", &use_writebarrier) - obj.Flagcount("x", "debug lexer", &Debug['x']) - obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) - var flag_shared int - var flag_dynlink bool - switch Thearch.Thechar { - case '5', '6', '7', '8', '9': - obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) - } - if Thearch.Thechar == '6' { - obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) - } - switch Thearch.Thechar { - case '5', '6', '7', '8', '9': - flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") - } - obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) - obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) - obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) - flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code") - obj.Flagparse(usage) - - if flag_dynlink { - flag_shared = 1 - } - Ctxt.Flag_shared = int32(flag_shared) - 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() - - if flag_race != 0 { - racepkg = mkpkg("runtime/race") - racepkg.Name = "race" - } - if flag_msan != 0 { - msanpkg = mkpkg("runtime/msan") - msanpkg.Name = "msan" - } - if flag_race != 0 && flag_msan != 0 { - log.Fatal("cannot use both -race and -msan") - } else if flag_race != 0 || flag_msan != 0 { - 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() - if Widthptr == 0 { - Fatalf("betypeinit failed") - } - - lexinit() - typeinit() - lexinit1() - - blockgen = 1 - dclcontext = PEXTERN - nerrors = 0 - lexlineno = 1 - - loadsys() - - for _, infile = range flag.Args() { - if trace && Debug['x'] != 0 { - fmt.Printf("--- %s ---\n", infile) - } - - linehistpush(infile) - - f, err := os.Open(infile) - if err != nil { - fmt.Printf("open %s: %v\n", infile, err) - errorexit() - } - bin := bufio.NewReader(f) - - // Skip initial BOM if present. - if r, _, _ := bin.ReadRune(); r != BOM { - bin.UnreadRune() - } - - block = 1 - iota_ = -1000000 - - imported_unsafe = false - - parse_file(bin) - 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() - f.Close() - } - - testdclstack() - mkpackage(localpkg.Name) // final import not used checks - lexfini() - - 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. - for i := 0; i < len(xtop); i++ { - if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 { - typecheck(&xtop[i], Etop) - } - } - - // Phase 2: Variable assignments. - // To check interface assignments, depends on phase 1. - - // Don't use range--typecheck can add closures to xtop. - for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 { - typecheck(&xtop[i], Etop) - } - } - resumecheckwidth() - - // Phase 3: Type check function bodies. - // Don't use range--typecheck can add closures to xtop. - for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE { - Curfn = xtop[i] - decldepth = 1 - saveerrors() - typechecklist(Curfn.Nbody.Slice(), Etop) - checkreturn(Curfn) - if nerrors != 0 { - Curfn.Nbody.Set(nil) // type errors; do not compile - } - } - } - - // Phase 4: Decide how to capture closed variables. - // This needs to run before escape analysis, - // because variables captured by value do not escape. - 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 - if Debug['l'] > 1 { - // Typecheck imported function bodies if debug['l'] > 1, - // otherwise lazily when used or re-exported. - for _, n := range importlist { - if len(n.Func.Inl.Slice()) != 0 { - 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) { - // TODO: use a range statement here if the order does not matter - for i := len(list) - 1; i >= 0; i-- { - n := list[i] - if n.Op == ODCLFUNC { - caninl(n) - inlcalls(n) - } - } - }) - } - - // 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. - 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. - 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. - for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCLFUNC { - funccompile(xtop[i]) - } - } - - if nsavederrors+nerrors == 0 { - fninit(xtop) - } - - if compiling_runtime != 0 { - checknowritebarrierrec() - } - - // Phase 9: Check external declarations. - for i, n := range externdcl { - if n.Op == ONAME { - typecheck(&externdcl[i], Erv) - } - } - - if nerrors+nsavederrors != 0 { - errorexit() - } - - dumpobj() - - if asmhdr != "" { - dumpasmhdr() - } - - if nerrors+nsavederrors != 0 { - errorexit() - } - - Flusherrors() -} - -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 != "!\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, "/") || - Ctxt.Windows != 0 && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || - strings.HasPrefix(name, "./") || name == "." || - strings.HasPrefix(name, "../") || name == ".." -} - -func findpkg(name string) (file string, ok bool) { - if islocalname(name) { - if safemode != 0 || nolocalimports != 0 { - 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 { - Yyerror("non-canonical import path %q (should be %q)", name, q) - 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 - } - } - - if goroot != "" { - suffix := "" - suffixsep := "" - if flag_installsuffix != "" { - suffixsep = "_" - suffix = flag_installsuffix - } else if flag_race != 0 { - suffixsep = "_" - suffix = "race" - } else if flag_msan != 0 { - suffixsep = "_" - suffix = "msan" - } - - file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name) - if _, err := os.Stat(file); err == nil { - return file, true - } - file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name) - 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 - incannedimport = 1 - - importpkg = Runtimepkg - parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil) - - importpkg = unsafepkg - parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil) - - importpkg = nil - incannedimport = 0 -} - -func importfile(f *Val, indent []byte) { - if importpkg != nil { - Fatalf("importpkg not nil") - } - - path_, ok := f.U.(string) - if !ok { - Yyerror("import statement not a string") - return - } - - if len(path_) == 0 { - Yyerror("import path is empty") - 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" { - Yyerror("cannot import \"main\"") - errorexit() - } - - if myimportpath != "" && path_ == myimportpath { - Yyerror("import %q while compiling that package (import cycle)", path_) - errorexit() - } - - if mapped, ok := importMap[path_]; ok { - path_ = mapped - } - - if path_ == "unsafe" { - if safemode != 0 { - Yyerror("cannot import package unsafe") - errorexit() - } - - importpkg = unsafepkg - imported_unsafe = true - return - } - - if islocalname(path_) { - if path_[0] == '/' { - Yyerror("import path cannot be absolute path") - return - } - - prefix := Ctxt.Pathname - if localimport != "" { - prefix = localimport - } - path_ = path.Join(prefix, path_) - - if isbadimport(path_) { - return - } - } - - file, found := findpkg(path_) - if !found { - Yyerror("can't find import: %q", path_) - errorexit() - } - - importpkg = mkpkg(path_) - - if importpkg.Imported { - return - } - - importpkg.Imported = true - - impf, err := os.Open(file) - if err != nil { - Yyerror("can't open import: %q: %v", path_, err) - errorexit() - } - defer impf.Close() - imp := bufio.NewReader(impf) - - if strings.HasSuffix(file, ".a") { - if !skiptopkgdef(imp) { - Yyerror("import %s: not a package file", file) - 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 ") { - Yyerror("import %s: not a go object file", file) - errorexit() - } - - q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) - if p[10:] != q { - Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) - errorexit() - } - } - - // 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: - // $$\n (old format): position the input right after $$\n and return - // $$B\n (new format): import directly, then feed the lexer a dummy statement - - // 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': - // old export format - parse_import(imp, indent) - - case 'B': - // new export format - imp.ReadByte() // skip \n after $$B - Import(imp) - - default: - Yyerror("no import in %q", path_) - errorexit() - } - - if safemode != 0 && !importpkg.Safe { - Yyerror("cannot import unsafe package %q", importpkg.Path) - } -} - func isSpace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' || c == '\r' } @@ -2037,349 +1212,3 @@ func (l *lexer) hexchar(n int) uint32 { return x } - -var basicTypes = [...]struct { - name string - etype EType -}{ - {"int8", TINT8}, - {"int16", TINT16}, - {"int32", TINT32}, - {"int64", TINT64}, - {"uint8", TUINT8}, - {"uint16", TUINT16}, - {"uint32", TUINT32}, - {"uint64", TUINT64}, - {"float32", TFLOAT32}, - {"float64", TFLOAT64}, - {"complex64", TCOMPLEX64}, - {"complex128", TCOMPLEX128}, - {"bool", TBOOL}, - {"string", TSTRING}, - {"any", TANY}, -} - -var typedefs = [...]struct { - name string - etype EType - width *int - sameas32 EType - sameas64 EType -}{ - {"int", TINT, &Widthint, TINT32, TINT64}, - {"uint", TUINT, &Widthint, TUINT32, TUINT64}, - {"uintptr", TUINTPTR, &Widthptr, TUINT32, TUINT64}, -} - -var builtinFuncs = [...]struct { - name string - op Op -}{ - {"append", OAPPEND}, - {"cap", OCAP}, - {"close", OCLOSE}, - {"complex", OCOMPLEX}, - {"copy", OCOPY}, - {"delete", ODELETE}, - {"imag", OIMAG}, - {"len", OLEN}, - {"make", OMAKE}, - {"new", ONEW}, - {"panic", OPANIC}, - {"print", OPRINT}, - {"println", OPRINTN}, - {"real", OREAL}, - {"recover", ORECOVER}, -} - -// lexinit initializes known symbols and the basic types. -func lexinit() { - for _, s := range basicTypes { - etype := s.etype - if int(etype) >= len(Types) { - Fatalf("lexinit: %s bad etype", s.name) - } - s2 := Pkglookup(s.name, builtinpkg) - t := Types[etype] - if t == nil { - t = typ(etype) - t.Sym = s2 - if etype != TANY && etype != TSTRING { - dowidth(t) - } - Types[etype] = t - } - s2.Def = typenod(t) - s2.Def.Name = new(Name) - } - - for _, s := range builtinFuncs { - // TODO(marvin): Fix Node.EType type union. - s2 := Pkglookup(s.name, builtinpkg) - s2.Def = Nod(ONAME, nil, nil) - s2.Def.Sym = s2 - s2.Def.Etype = EType(s.op) - } - - idealstring = typ(TSTRING) - idealbool = typ(TBOOL) - - s := Pkglookup("true", builtinpkg) - s.Def = Nodbool(true) - s.Def.Sym = Lookup("true") - s.Def.Name = new(Name) - s.Def.Type = idealbool - - s = Pkglookup("false", builtinpkg) - s.Def = Nodbool(false) - s.Def.Sym = Lookup("false") - s.Def.Name = new(Name) - s.Def.Type = idealbool - - s = Lookup("_") - s.Block = -100 - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - Types[TBLANK] = typ(TBLANK) - s.Def.Type = Types[TBLANK] - nblank = s.Def - - s = Pkglookup("_", builtinpkg) - s.Block = -100 - s.Def = Nod(ONAME, nil, nil) - s.Def.Sym = s - Types[TBLANK] = typ(TBLANK) - s.Def.Type = Types[TBLANK] - - Types[TNIL] = typ(TNIL) - s = Pkglookup("nil", builtinpkg) - var v Val - v.U = new(NilVal) - s.Def = nodlit(v) - s.Def.Sym = s - s.Def.Name = new(Name) - - s = Pkglookup("iota", builtinpkg) - s.Def = Nod(OIOTA, nil, nil) - s.Def.Sym = s - s.Def.Name = new(Name) -} - -func lexinit1() { - // t = interface { Error() string } - rcvr := typ(TSTRUCT) - - rcvr.Type = typ(TFIELD) - rcvr.Type.Type = Ptrto(typ(TSTRUCT)) - rcvr.Funarg = true - in := typ(TSTRUCT) - in.Funarg = true - out := typ(TSTRUCT) - out.Type = typ(TFIELD) - out.Type.Type = Types[TSTRING] - out.Funarg = true - f := typ(TFUNC) - *f.RecvsP() = rcvr - *f.ResultsP() = out - *f.ParamsP() = in - f.Thistuple = 1 - f.Intuple = 0 - f.Outnamed = false - f.Outtuple = 1 - t := typ(TINTER) - t.Type = typ(TFIELD) - t.Type.Sym = Lookup("Error") - t.Type.Type = f - - // error type - s := Pkglookup("error", builtinpkg) - errortype = t - errortype.Sym = s - s.Def = typenod(errortype) - - // byte alias - s = Pkglookup("byte", builtinpkg) - bytetype = typ(TUINT8) - bytetype.Sym = s - s.Def = typenod(bytetype) - s.Def.Name = new(Name) - - // rune alias - s = Pkglookup("rune", builtinpkg) - runetype = typ(TINT32) - runetype.Sym = s - s.Def = typenod(runetype) - s.Def.Name = new(Name) - - // backend-dependent builtin types (e.g. int). - for _, s := range typedefs { - s1 := Pkglookup(s.name, builtinpkg) - - sameas := s.sameas32 - if *s.width == 8 { - sameas = s.sameas64 - } - - Simtype[s.etype] = sameas - minfltval[s.etype] = minfltval[sameas] - maxfltval[s.etype] = maxfltval[sameas] - Minintval[s.etype] = Minintval[sameas] - Maxintval[s.etype] = Maxintval[sameas] - - t := typ(s.etype) - t.Sym = s1 - Types[s.etype] = t - s1.Def = typenod(t) - s1.Def.Name = new(Name) - s1.Origpkg = builtinpkg - - dowidth(t) - } -} - -func lexfini() { - for _, s := range builtinpkg.Syms { - if s.Def == nil || (s.Name == "any" && Debug['A'] == 0) { - continue - } - s1 := Lookup(s.Name) - if s1.Def != nil { - continue - } - - s1.Def = s.Def - s1.Block = s.Block - } - - nodfp = Nod(ONAME, nil, nil) - nodfp.Type = Types[TINT32] - nodfp.Xoffset = 0 - nodfp.Class = PPARAM - nodfp.Sym = Lookup(".fp") -} - -var lexn = map[rune]string{ - LNAME: "NAME", - LLITERAL: "LITERAL", - - LOPER: "OPER", - LASOP: "ASOP", - LINCOP: "INCOP", - - LCOLAS: "COLAS", - LCOMM: "COMM", - LDDD: "DDD", - - LBREAK: "BREAK", - LCASE: "CASE", - LCHAN: "CHAN", - LCONST: "CONST", - LCONTINUE: "CONTINUE", - LDEFAULT: "DEFAULT", - LDEFER: "DEFER", - LELSE: "ELSE", - LFALL: "FALL", - LFOR: "FOR", - LFUNC: "FUNC", - LGO: "GO", - LGOTO: "GOTO", - LIF: "IF", - LIMPORT: "IMPORT", - LINTERFACE: "INTERFACE", - LMAP: "MAP", - LPACKAGE: "PACKAGE", - LRANGE: "RANGE", - LRETURN: "RETURN", - LSELECT: "SELECT", - LSTRUCT: "STRUCT", - LSWITCH: "SWITCH", - LTYPE: "TYPE", - LVAR: "VAR", -} - -func lexname(lex rune) string { - if s, ok := lexn[lex]; ok { - return s - } - return fmt.Sprintf("LEX-%d", lex) -} - -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 == "_" { - Yyerror("invalid package name _") - } - localpkg.Name = pkgname - } else { - if pkgname != localpkg.Name { - Yyerror("package %s; expected %s", pkgname, localpkg.Name) - } - 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:] - } - if Ctxt.Windows != 0 { - if i := strings.LastIndex(p, `\`); i >= 0 { - p = p[i+1:] - } - } - if i := strings.LastIndex(p, "."); i >= 0 { - p = p[:i] - } - suffix := ".o" - if writearchive > 0 { - suffix = ".a" - } - outfile = p + suffix - } -} diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go new file mode 100644 index 0000000000..fb6a940864 --- /dev/null +++ b/src/cmd/compile/internal/gc/main.go @@ -0,0 +1,1184 @@ +// 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" + "cmd/compile/internal/ssa" + "cmd/internal/obj" + "flag" + "fmt" + "io" + "log" + "os" + "path" + "strconv" + "strings" +) + +var imported_unsafe bool + +var ( + goos string + goarch string + goroot string + buildid string +) + +var ( + Debug_append int + Debug_panic int + Debug_slice int + Debug_wb int +) + +// 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 + {"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 = " " + } + fmt.Printf("compile version %s%s%s\n", obj.Getgoversion(), sep, p) + os.Exit(0) +} + +func Main() { + defer hidePanic() + + // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, + // but not other values. + p := obj.Getgoarch() + + if !strings.HasPrefix(p, Thearch.Thestring) { + log.Fatalf("cannot use %cg with GOARCH=%s", Thearch.Thechar, p) + } + goarch = p + + Thearch.Linkarchinit() + Ctxt = obj.Linknew(Thearch.Thelinkarch) + Ctxt.DiagFunc = Yyerror + Ctxt.Bso = &bstdout + bstdout = *obj.Binitw(os.Stdout) + + 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 + gostringpkg = mkpkg("go.string") + + gostringpkg.Name = "go.string" + gostringpkg.Prefix = "go.string" // not go%2estring + + itabpkg = mkpkg("go.itab") + + itabpkg.Name = "go.itab" + itabpkg.Prefix = "go.itab" // not go%2eitab + + typelinkpkg = mkpkg("go.typelink") + typelinkpkg.Name = "go.typelink" + typelinkpkg.Prefix = "go.typelink" // not go%2etypelink + + trackpkg = mkpkg("go.track") + + trackpkg.Name = "go.track" + trackpkg.Prefix = "go.track" // not go%2etrack + + typepkg = mkpkg("type") + + typepkg.Name = "type" + + goroot = obj.Getgoroot() + goos = obj.Getgoos() + + Nacl = goos == "nacl" + if Nacl { + flag_largemodel = 1 + } + + outfile = "" + obj.Flagcount("+", "compiling runtime", &compiling_runtime) + 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']) + obj.Flagstr("D", "set relative `path` for local imports", &localimport) + 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("L", "use full (long) path in error messages", &Debug['L']) + 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']) + obj.Flagstr("asmhdr", "write assembly header to `file`", &asmhdr) + obj.Flagstr("buildid", "record `id` as the build id in the export metadata", &buildid) + obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) + obj.Flagstr("d", "print debug information about items in `list`", &debugstr) + 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) + obj.Flagstr("installsuffix", "set pkg directory `suffix`", &flag_installsuffix) + obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) + obj.Flagcount("l", "disable inlining", &Debug['l']) + obj.Flagcount("live", "debug liveness analysis", &debuglive) + obj.Flagcount("m", "print optimization decisions", &Debug['m']) + obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan) + obj.Flagcount("newexport", "use new export format", &newexport) // TODO(gri) remove eventually (issue 13241) + obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) + obj.Flagstr("o", "write output to `file`", &outfile) + obj.Flagstr("p", "set expected package import `path`", &myimportpath) + obj.Flagcount("pack", "write package file instead of object file", &writearchive) + obj.Flagcount("r", "debug generated wrappers", &Debug['r']) + obj.Flagcount("race", "enable race detector", &flag_race) + obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) + obj.Flagstr("trimpath", "remove `prefix` from recorded source file paths", &Ctxt.LineHist.TrimPathPrefix) + obj.Flagcount("u", "reject unsafe code", &safemode) + obj.Flagcount("v", "increase debug verbosity", &Debug['v']) + obj.Flagcount("w", "debug type checking", &Debug['w']) + use_writebarrier = 1 + obj.Flagcount("wb", "enable write barrier", &use_writebarrier) + obj.Flagcount("x", "debug lexer", &Debug['x']) + obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y']) + var flag_shared int + var flag_dynlink bool + switch Thearch.Thechar { + case '5', '6', '7', '8', '9': + obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) + } + if Thearch.Thechar == '6' { + obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) + } + switch Thearch.Thechar { + case '5', '6', '7', '8', '9': + flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") + } + obj.Flagstr("cpuprofile", "write cpu profile to `file`", &cpuprofile) + obj.Flagstr("memprofile", "write memory profile to `file`", &memprofile) + obj.Flagint64("memprofilerate", "set runtime.MemProfileRate to `rate`", &memprofilerate) + flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code") + obj.Flagparse(usage) + + if flag_dynlink { + flag_shared = 1 + } + Ctxt.Flag_shared = int32(flag_shared) + 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() + + if flag_race != 0 { + racepkg = mkpkg("runtime/race") + racepkg.Name = "race" + } + if flag_msan != 0 { + msanpkg = mkpkg("runtime/msan") + msanpkg.Name = "msan" + } + if flag_race != 0 && flag_msan != 0 { + log.Fatal("cannot use both -race and -msan") + } else if flag_race != 0 || flag_msan != 0 { + 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() + if Widthptr == 0 { + Fatalf("betypeinit failed") + } + + lexinit() + typeinit() + lexinit1() + + blockgen = 1 + dclcontext = PEXTERN + nerrors = 0 + lexlineno = 1 + + loadsys() + + for _, infile = range flag.Args() { + if trace && Debug['x'] != 0 { + fmt.Printf("--- %s ---\n", infile) + } + + linehistpush(infile) + + f, err := os.Open(infile) + if err != nil { + fmt.Printf("open %s: %v\n", infile, err) + errorexit() + } + bin := bufio.NewReader(f) + + // Skip initial BOM if present. + if r, _, _ := bin.ReadRune(); r != BOM { + bin.UnreadRune() + } + + block = 1 + iota_ = -1000000 + + imported_unsafe = false + + parse_file(bin) + 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() + f.Close() + } + + testdclstack() + mkpackage(localpkg.Name) // final import not used checks + lexfini() + + 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. + for i := 0; i < len(xtop); i++ { + if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 { + typecheck(&xtop[i], Etop) + } + } + + // Phase 2: Variable assignments. + // To check interface assignments, depends on phase 1. + + // Don't use range--typecheck can add closures to xtop. + for i := 0; i < len(xtop); i++ { + if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 { + typecheck(&xtop[i], Etop) + } + } + resumecheckwidth() + + // Phase 3: Type check function bodies. + // Don't use range--typecheck can add closures to xtop. + for i := 0; i < len(xtop); i++ { + if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE { + Curfn = xtop[i] + decldepth = 1 + saveerrors() + typechecklist(Curfn.Nbody.Slice(), Etop) + checkreturn(Curfn) + if nerrors != 0 { + Curfn.Nbody.Set(nil) // type errors; do not compile + } + } + } + + // Phase 4: Decide how to capture closed variables. + // This needs to run before escape analysis, + // because variables captured by value do not escape. + 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 + if Debug['l'] > 1 { + // Typecheck imported function bodies if debug['l'] > 1, + // otherwise lazily when used or re-exported. + for _, n := range importlist { + if len(n.Func.Inl.Slice()) != 0 { + 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) { + // TODO: use a range statement here if the order does not matter + for i := len(list) - 1; i >= 0; i-- { + n := list[i] + if n.Op == ODCLFUNC { + caninl(n) + inlcalls(n) + } + } + }) + } + + // 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. + 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. + 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. + for i := 0; i < len(xtop); i++ { + if xtop[i].Op == ODCLFUNC { + funccompile(xtop[i]) + } + } + + if nsavederrors+nerrors == 0 { + fninit(xtop) + } + + if compiling_runtime != 0 { + checknowritebarrierrec() + } + + // Phase 9: Check external declarations. + for i, n := range externdcl { + if n.Op == ONAME { + typecheck(&externdcl[i], Erv) + } + } + + if nerrors+nsavederrors != 0 { + errorexit() + } + + dumpobj() + + if asmhdr != "" { + dumpasmhdr() + } + + if nerrors+nsavederrors != 0 { + errorexit() + } + + Flusherrors() +} + +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 != "!\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, "/") || + Ctxt.Windows != 0 && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || + strings.HasPrefix(name, "./") || name == "." || + strings.HasPrefix(name, "../") || name == ".." +} + +func findpkg(name string) (file string, ok bool) { + if islocalname(name) { + if safemode != 0 || nolocalimports != 0 { + 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 { + Yyerror("non-canonical import path %q (should be %q)", name, q) + 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 + } + } + + if goroot != "" { + suffix := "" + suffixsep := "" + if flag_installsuffix != "" { + suffixsep = "_" + suffix = flag_installsuffix + } else if flag_race != 0 { + suffixsep = "_" + suffix = "race" + } else if flag_msan != 0 { + suffixsep = "_" + suffix = "msan" + } + + file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name) + if _, err := os.Stat(file); err == nil { + return file, true + } + file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name) + 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 + incannedimport = 1 + + importpkg = Runtimepkg + parse_import(bufio.NewReader(strings.NewReader(runtimeimport)), nil) + + importpkg = unsafepkg + parse_import(bufio.NewReader(strings.NewReader(unsafeimport)), nil) + + importpkg = nil + incannedimport = 0 +} + +func importfile(f *Val, indent []byte) { + if importpkg != nil { + Fatalf("importpkg not nil") + } + + path_, ok := f.U.(string) + if !ok { + Yyerror("import statement not a string") + return + } + + if len(path_) == 0 { + Yyerror("import path is empty") + 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" { + Yyerror("cannot import \"main\"") + errorexit() + } + + if myimportpath != "" && path_ == myimportpath { + Yyerror("import %q while compiling that package (import cycle)", path_) + errorexit() + } + + if mapped, ok := importMap[path_]; ok { + path_ = mapped + } + + if path_ == "unsafe" { + if safemode != 0 { + Yyerror("cannot import package unsafe") + errorexit() + } + + importpkg = unsafepkg + imported_unsafe = true + return + } + + if islocalname(path_) { + if path_[0] == '/' { + Yyerror("import path cannot be absolute path") + return + } + + prefix := Ctxt.Pathname + if localimport != "" { + prefix = localimport + } + path_ = path.Join(prefix, path_) + + if isbadimport(path_) { + return + } + } + + file, found := findpkg(path_) + if !found { + Yyerror("can't find import: %q", path_) + errorexit() + } + + importpkg = mkpkg(path_) + + if importpkg.Imported { + return + } + + importpkg.Imported = true + + impf, err := os.Open(file) + if err != nil { + Yyerror("can't open import: %q: %v", path_, err) + errorexit() + } + defer impf.Close() + imp := bufio.NewReader(impf) + + if strings.HasSuffix(file, ".a") { + if !skiptopkgdef(imp) { + Yyerror("import %s: not a package file", file) + 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 ") { + Yyerror("import %s: not a go object file", file) + errorexit() + } + + q := fmt.Sprintf("%s %s %s %s", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) + if p[10:] != q { + Yyerror("import %s: object is [%s] expected [%s]", file, p[10:], q) + errorexit() + } + } + + // 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: + // $$\n (old format): position the input right after $$\n and return + // $$B\n (new format): import directly, then feed the lexer a dummy statement + + // 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': + // old export format + parse_import(imp, indent) + + case 'B': + // new export format + imp.ReadByte() // skip \n after $$B + Import(imp) + + default: + Yyerror("no import in %q", path_) + errorexit() + } + + if safemode != 0 && !importpkg.Safe { + Yyerror("cannot import unsafe package %q", importpkg.Path) + } +} + +var basicTypes = [...]struct { + name string + etype EType +}{ + {"int8", TINT8}, + {"int16", TINT16}, + {"int32", TINT32}, + {"int64", TINT64}, + {"uint8", TUINT8}, + {"uint16", TUINT16}, + {"uint32", TUINT32}, + {"uint64", TUINT64}, + {"float32", TFLOAT32}, + {"float64", TFLOAT64}, + {"complex64", TCOMPLEX64}, + {"complex128", TCOMPLEX128}, + {"bool", TBOOL}, + {"string", TSTRING}, + {"any", TANY}, +} + +var typedefs = [...]struct { + name string + etype EType + width *int + sameas32 EType + sameas64 EType +}{ + {"int", TINT, &Widthint, TINT32, TINT64}, + {"uint", TUINT, &Widthint, TUINT32, TUINT64}, + {"uintptr", TUINTPTR, &Widthptr, TUINT32, TUINT64}, +} + +var builtinFuncs = [...]struct { + name string + op Op +}{ + {"append", OAPPEND}, + {"cap", OCAP}, + {"close", OCLOSE}, + {"complex", OCOMPLEX}, + {"copy", OCOPY}, + {"delete", ODELETE}, + {"imag", OIMAG}, + {"len", OLEN}, + {"make", OMAKE}, + {"new", ONEW}, + {"panic", OPANIC}, + {"print", OPRINT}, + {"println", OPRINTN}, + {"real", OREAL}, + {"recover", ORECOVER}, +} + +// lexinit initializes known symbols and the basic types. +func lexinit() { + for _, s := range basicTypes { + etype := s.etype + if int(etype) >= len(Types) { + Fatalf("lexinit: %s bad etype", s.name) + } + s2 := Pkglookup(s.name, builtinpkg) + t := Types[etype] + if t == nil { + t = typ(etype) + t.Sym = s2 + if etype != TANY && etype != TSTRING { + dowidth(t) + } + Types[etype] = t + } + s2.Def = typenod(t) + s2.Def.Name = new(Name) + } + + for _, s := range builtinFuncs { + // TODO(marvin): Fix Node.EType type union. + s2 := Pkglookup(s.name, builtinpkg) + s2.Def = Nod(ONAME, nil, nil) + s2.Def.Sym = s2 + s2.Def.Etype = EType(s.op) + } + + idealstring = typ(TSTRING) + idealbool = typ(TBOOL) + + s := Pkglookup("true", builtinpkg) + s.Def = Nodbool(true) + s.Def.Sym = Lookup("true") + s.Def.Name = new(Name) + s.Def.Type = idealbool + + s = Pkglookup("false", builtinpkg) + s.Def = Nodbool(false) + s.Def.Sym = Lookup("false") + s.Def.Name = new(Name) + s.Def.Type = idealbool + + s = Lookup("_") + s.Block = -100 + s.Def = Nod(ONAME, nil, nil) + s.Def.Sym = s + Types[TBLANK] = typ(TBLANK) + s.Def.Type = Types[TBLANK] + nblank = s.Def + + s = Pkglookup("_", builtinpkg) + s.Block = -100 + s.Def = Nod(ONAME, nil, nil) + s.Def.Sym = s + Types[TBLANK] = typ(TBLANK) + s.Def.Type = Types[TBLANK] + + Types[TNIL] = typ(TNIL) + s = Pkglookup("nil", builtinpkg) + var v Val + v.U = new(NilVal) + s.Def = nodlit(v) + s.Def.Sym = s + s.Def.Name = new(Name) + + s = Pkglookup("iota", builtinpkg) + s.Def = Nod(OIOTA, nil, nil) + s.Def.Sym = s + s.Def.Name = new(Name) +} + +func lexinit1() { + // t = interface { Error() string } + rcvr := typ(TSTRUCT) + + rcvr.Type = typ(TFIELD) + rcvr.Type.Type = Ptrto(typ(TSTRUCT)) + rcvr.Funarg = true + in := typ(TSTRUCT) + in.Funarg = true + out := typ(TSTRUCT) + out.Type = typ(TFIELD) + out.Type.Type = Types[TSTRING] + out.Funarg = true + f := typ(TFUNC) + *f.RecvsP() = rcvr + *f.ResultsP() = out + *f.ParamsP() = in + f.Thistuple = 1 + f.Intuple = 0 + f.Outnamed = false + f.Outtuple = 1 + t := typ(TINTER) + t.Type = typ(TFIELD) + t.Type.Sym = Lookup("Error") + t.Type.Type = f + + // error type + s := Pkglookup("error", builtinpkg) + errortype = t + errortype.Sym = s + s.Def = typenod(errortype) + + // byte alias + s = Pkglookup("byte", builtinpkg) + bytetype = typ(TUINT8) + bytetype.Sym = s + s.Def = typenod(bytetype) + s.Def.Name = new(Name) + + // rune alias + s = Pkglookup("rune", builtinpkg) + runetype = typ(TINT32) + runetype.Sym = s + s.Def = typenod(runetype) + s.Def.Name = new(Name) + + // backend-dependent builtin types (e.g. int). + for _, s := range typedefs { + s1 := Pkglookup(s.name, builtinpkg) + + sameas := s.sameas32 + if *s.width == 8 { + sameas = s.sameas64 + } + + Simtype[s.etype] = sameas + minfltval[s.etype] = minfltval[sameas] + maxfltval[s.etype] = maxfltval[sameas] + Minintval[s.etype] = Minintval[sameas] + Maxintval[s.etype] = Maxintval[sameas] + + t := typ(s.etype) + t.Sym = s1 + Types[s.etype] = t + s1.Def = typenod(t) + s1.Def.Name = new(Name) + s1.Origpkg = builtinpkg + + dowidth(t) + } +} + +func lexfini() { + for _, s := range builtinpkg.Syms { + if s.Def == nil || (s.Name == "any" && Debug['A'] == 0) { + continue + } + s1 := Lookup(s.Name) + if s1.Def != nil { + continue + } + + s1.Def = s.Def + s1.Block = s.Block + } + + nodfp = Nod(ONAME, nil, nil) + nodfp.Type = Types[TINT32] + nodfp.Xoffset = 0 + nodfp.Class = PPARAM + nodfp.Sym = Lookup(".fp") +} + +var lexn = map[rune]string{ + LNAME: "NAME", + LLITERAL: "LITERAL", + + LOPER: "OPER", + LASOP: "ASOP", + LINCOP: "INCOP", + + LCOLAS: "COLAS", + LCOMM: "COMM", + LDDD: "DDD", + + LBREAK: "BREAK", + LCASE: "CASE", + LCHAN: "CHAN", + LCONST: "CONST", + LCONTINUE: "CONTINUE", + LDEFAULT: "DEFAULT", + LDEFER: "DEFER", + LELSE: "ELSE", + LFALL: "FALL", + LFOR: "FOR", + LFUNC: "FUNC", + LGO: "GO", + LGOTO: "GOTO", + LIF: "IF", + LIMPORT: "IMPORT", + LINTERFACE: "INTERFACE", + LMAP: "MAP", + LPACKAGE: "PACKAGE", + LRANGE: "RANGE", + LRETURN: "RETURN", + LSELECT: "SELECT", + LSTRUCT: "STRUCT", + LSWITCH: "SWITCH", + LTYPE: "TYPE", + LVAR: "VAR", +} + +func lexname(lex rune) string { + if s, ok := lexn[lex]; ok { + return s + } + return fmt.Sprintf("LEX-%d", lex) +} + +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 == "_" { + Yyerror("invalid package name _") + } + localpkg.Name = pkgname + } else { + if pkgname != localpkg.Name { + Yyerror("package %s; expected %s", pkgname, localpkg.Name) + } + 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:] + } + if Ctxt.Windows != 0 { + if i := strings.LastIndex(p, `\`); i >= 0 { + p = p[i+1:] + } + } + if i := strings.LastIndex(p, "."); i >= 0 { + p = p[:i] + } + suffix := ".o" + if writearchive > 0 { + suffix = ".a" + } + outfile = p + suffix + } +}