// NewConfig returns a new configuration object for the given architecture. func NewConfig(arch string, fe Frontend, ctxt *obj.Link, optimize bool) *Config { c := &Config{arch: arch, fe: fe} switch arch { case "amd64": c.IntSize = 8 c.PtrSize = 8 c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 c.registers = registersAMD64[:] case "386": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support case "arm": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlockARM c.lowerValue = rewriteValueARM c.registers = registersARM[:] default: fe.Unimplementedf(0, "arch %s not implemented", arch) } c.ctxt = ctxt c.optimize = optimize // Don't use Duff's device on Plan 9, because floating // point operations are not allowed in note handler. if obj.Getgoos() == "plan9" { c.noDuffDevice = true } // Assign IDs to preallocated values/blocks. for i := range c.values { c.values[i].ID = ID(i) } for i := range c.blocks { c.blocks[i].ID = ID(i) } c.logfiles = make(map[string]*os.File) // cutoff is compared with product of numblocks and numvalues, // if product is smaller than cutoff, use old non-sparse method. // cutoff == 0 implies all sparse. // cutoff == -1 implies none sparse. // Good cutoff values seem to be O(million) depending on constant factor cost of sparse. // TODO: get this from a flag, not an environment variable c.sparsePhiCutoff = 2500000 // 0 for testing. // 2500000 determined with crude experiments w/ make.bash ev := os.Getenv("GO_SSA_PHI_LOC_CUTOFF") if ev != "" { v, err := strconv.ParseInt(ev, 10, 64) if err != nil { fe.Fatalf(0, "Environment variable GO_SSA_PHI_LOC_CUTOFF (value '%s') did not parse as a number", ev) } c.sparsePhiCutoff = uint64(v) // convert -1 to maxint, for never use sparse } return c }
func betypeinit() { if obj.Getgoarch() == "amd64p32" { addptr = x86.AADDL movptr = x86.AMOVL leaptr = x86.ALEAL cmpptr = x86.ACMPL } if gc.Ctxt.Flag_dynlink || obj.Getgoos() == "nacl" { resvd = append(resvd, x86.REG_R15) } if gc.Ctxt.Framepointer_enabled || obj.Getgoos() == "nacl" { resvd = append(resvd, x86.REG_BP) } gc.Thearch.ReservedRegs = resvd }
func Ldmain() { Bso = bufio.NewWriter(os.Stdout) Ctxt = linknew(SysArch) Ctxt.Diag = Diag Ctxt.Bso = Bso Debug = [128]int{} nerrors = 0 outfile = "" HEADTYPE = -1 INITTEXT = -1 INITDAT = -1 INITRND = -1 INITENTRY = "" Linkmode = LinkAuto // For testing behavior of go command when tools crash silently. // Undocumented, not in standard flag parser to avoid // exposing in usage message. for _, arg := range os.Args { if arg == "-crash_for_testing" { os.Exit(2) } } if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" { obj.Flagcount("8", "use 64-bit addresses in symbol table", &Debug['8']) } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) obj.Flagcount("C", "check Go calls to C code", &Debug['C']) obj.Flagint64("D", "set data segment `address`", &INITDAT) obj.Flagstr("E", "set `entry` symbol name", &INITENTRY) obj.Flagfn1("I", "use `linker` as ELF dynamic linker", setinterp) obj.Flagfn1("L", "add specified `directory` to library path", Lflag) obj.Flagfn1("H", "set header `type`", setheadtype) obj.Flagint32("R", "set address rounding `quantum`", &INITRND) obj.Flagint64("T", "set text segment `address`", &INITTEXT) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", addstrdata1) obj.Flagcount("a", "disassemble output", &Debug['a']) obj.Flagstr("buildid", "record `id` as Go toolchain build id", &buildid) flag.Var(&Buildmode, "buildmode", "set build `mode`") obj.Flagcount("c", "dump call graph", &Debug['c']) obj.Flagcount("d", "disable dynamic executable", &Debug['d']) flag.BoolVar(&flag_dumpdep, "dumpdep", false, "dump symbol dependency graph") obj.Flagstr("extar", "archive program for buildmode=c-archive", &extar) obj.Flagstr("extld", "use `linker` when linking in external mode", &extld) obj.Flagstr("extldflags", "pass `flags` to external linker", &extldflags) obj.Flagcount("f", "ignore version mismatch", &Debug['f']) obj.Flagcount("g", "disable go package data checks", &Debug['g']) obj.Flagcount("h", "halt on error", &Debug['h']) obj.Flagstr("installsuffix", "set package directory `suffix`", &flag_installsuffix) obj.Flagstr("k", "set field tracking `symbol`", &tracksym) obj.Flagstr("libgcc", "compiler support lib for internal linking; use \"none\" to disable", &libgccfile) obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries") obj.Flagcount("msan", "enable MSan interface", &flag_msan) obj.Flagcount("n", "dump symbol table", &Debug['n']) obj.Flagstr("o", "write output to `file`", &outfile) flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...") obj.Flagcount("race", "enable race detector", &flag_race) obj.Flagcount("s", "disable symbol table", &Debug['s']) var flagShared int if SysArch.InFamily(sys.ARM, sys.AMD64) { obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &flagShared) } obj.Flagstr("tmpdir", "use `directory` for temporary files", &tmpdir) obj.Flagcount("u", "reject unsafe packages", &Debug['u']) obj.Flagcount("v", "print link trace", &Debug['v']) obj.Flagcount("w", "disable DWARF generation", &Debug['w']) 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) obj.Flagparse(usage) startProfile() Ctxt.Bso = Bso Ctxt.Debugvlog = int32(Debug['v']) if flagShared != 0 { if Buildmode == BuildmodeUnset { Buildmode = BuildmodeCShared } else if Buildmode != BuildmodeCShared { Exitf("-shared and -buildmode=%s are incompatible", Buildmode.String()) } } if Buildmode == BuildmodeUnset { Buildmode = BuildmodeExe } if Buildmode != BuildmodeShared && flag.NArg() != 1 { usage() } if outfile == "" { outfile = "a.out" if HEADTYPE == obj.Hwindows { outfile += ".exe" } } libinit() // creates outfile if HEADTYPE == -1 { HEADTYPE = int32(headtype(goos)) } Ctxt.Headtype = int(HEADTYPE) if headstring == "" { headstring = Headstr(int(HEADTYPE)) } Thearch.Archinit() if Linkshared && !Iself { Exitf("-linkshared can only be used on elf systems") } if Debug['v'] != 0 { fmt.Fprintf(Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) } Bso.Flush() if Buildmode == BuildmodeShared { for i := 0; i < flag.NArg(); i++ { arg := flag.Arg(i) parts := strings.SplitN(arg, "=", 2) var pkgpath, file string if len(parts) == 1 { pkgpath, file = "main", arg } else { pkgpath, file = parts[0], parts[1] } pkglistfornote = append(pkglistfornote, pkgpath...) pkglistfornote = append(pkglistfornote, '\n') addlibpath(Ctxt, "command line", "command line", file, pkgpath, "") } } else { addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main", "") } loadlib() checkstrdata() deadcode(Ctxt) fieldtrack(Ctxt) callgraph() doelf() if HEADTYPE == obj.Hdarwin { domacho() } dostkcheck() if HEADTYPE == obj.Hwindows { dope() } addexport() Thearch.Gentext() // trampolines, call stubs, etc. textbuildid() textaddress() pclntab() findfunctab() symtab() dodata() address() reloc() Thearch.Asmb() undef() hostlink() archive() if Debug['v'] != 0 { fmt.Fprintf(Bso, "%5.2f cpu time\n", obj.Cputime()) fmt.Fprintf(Bso, "%d symbols\n", len(Ctxt.Allsym)) fmt.Fprintf(Bso, "%d liveness data\n", liveness) } Bso.Flush() errorexit() }
func linknew(arch *sys.Arch) *Link { ctxt := &Link{ Hash: []map[string]*LSym{ // preallocate about 2mb for hash of // non static symbols make(map[string]*LSym, 100000), }, Allsym: make([]*LSym, 0, 100000), Arch: arch, Goroot: obj.Getgoroot(), } p := obj.Getgoarch() if p != arch.Name { log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) } ctxt.Headtype = headtype(obj.Getgoos()) if ctxt.Headtype < 0 { log.Fatalf("unknown goos %s", obj.Getgoos()) } // Record thread-local storage offset. // TODO(rsc): Move tlsoffset back into the linker. switch ctxt.Headtype { default: log.Fatalf("unknown thread-local storage offset for %s", Headstr(ctxt.Headtype)) case obj.Hplan9, obj.Hwindows: break /* * ELF uses TLS offset negative from FS. * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). * Known to low-level assembly in package runtime and runtime/cgo. */ case obj.Hlinux, obj.Hfreebsd, obj.Hnetbsd, obj.Hopenbsd, obj.Hdragonfly, obj.Hsolaris: if obj.Getgoos() == "android" { switch ctxt.Arch.Family { case sys.AMD64: // Android/amd64 constant - offset from 0(FS) to our TLS slot. // Explained in src/runtime/cgo/gcc_android_*.c ctxt.Tlsoffset = 0x1d0 case sys.I386: // Android/386 constant - offset from 0(GS) to our TLS slot. ctxt.Tlsoffset = 0xf8 default: ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize } } else { ctxt.Tlsoffset = -1 * ctxt.Arch.PtrSize } case obj.Hnacl: switch ctxt.Arch.Family { default: log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) case sys.ARM: ctxt.Tlsoffset = 0 case sys.AMD64: ctxt.Tlsoffset = 0 case sys.I386: ctxt.Tlsoffset = -8 } /* * OS X system constants - offset from 0(GS) to our TLS. * Explained in src/runtime/cgo/gcc_darwin_*.c. */ case obj.Hdarwin: switch ctxt.Arch.Family { default: log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) case sys.ARM: ctxt.Tlsoffset = 0 // dummy value, not needed case sys.AMD64: ctxt.Tlsoffset = 0x8a0 case sys.ARM64: ctxt.Tlsoffset = 0 // dummy value, not needed case sys.I386: ctxt.Tlsoffset = 0x468 } } // On arm, record goarm. if ctxt.Arch.Family == sys.ARM { ctxt.Goarm = obj.Getgoarm() } return ctxt }
} // zero old range p = zerorange(p, int64(frame), lo, hi) // set new range hi = n.Xoffset + n.Type.Width lo = n.Xoffset } // zero final range zerorange(p, int64(frame), lo, hi) } var darwin = obj.Getgoos() == "darwin" func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { cnt := hi - lo if cnt == 0 { return p } if cnt < int64(4*gc.Widthptr) { for i := int64(0); i < cnt; i += int64(gc.Widthptr) { p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i) } } else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0) p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0) p.Reg = arm64.REGRT1 p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
func blockcopy(n, ns *gc.Node, osrc, odst, w int64) { var noddi gc.Node gc.Nodreg(&noddi, gc.Types[gc.Tptr], x86.REG_DI) var nodsi gc.Node gc.Nodreg(&nodsi, gc.Types[gc.Tptr], x86.REG_SI) var nodl gc.Node var nodr gc.Node if n.Ullman >= ns.Ullman { gc.Agenr(n, &nodr, &nodsi) if ns.Op == gc.ONAME { gc.Gvardef(ns) } gc.Agenr(ns, &nodl, &noddi) } else { if ns.Op == gc.ONAME { gc.Gvardef(ns) } gc.Agenr(ns, &nodl, &noddi) gc.Agenr(n, &nodr, &nodsi) } if nodl.Reg != x86.REG_DI { gmove(&nodl, &noddi) } if nodr.Reg != x86.REG_SI { gmove(&nodr, &nodsi) } gc.Regfree(&nodl) gc.Regfree(&nodr) c := w % 8 // bytes q := w / 8 // quads var oldcx gc.Node var cx gc.Node savex(x86.REG_CX, &cx, &oldcx, nil, gc.Types[gc.TINT64]) // if we are copying forward on the stack and // the src and dst overlap, then reverse direction if osrc < odst && odst < osrc+w { // reverse direction gins(x86.ASTD, nil, nil) // set direction flag if c > 0 { gconreg(addptr, w-1, x86.REG_SI) gconreg(addptr, w-1, x86.REG_DI) gconreg(movptr, c, x86.REG_CX) gins(x86.AREP, nil, nil) // repeat gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- } if q > 0 { if c > 0 { gconreg(addptr, -7, x86.REG_SI) gconreg(addptr, -7, x86.REG_DI) } else { gconreg(addptr, w-8, x86.REG_SI) gconreg(addptr, w-8, x86.REG_DI) } gconreg(movptr, q, x86.REG_CX) gins(x86.AREP, nil, nil) // repeat gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)-,*(DI)- } // we leave with the flag clear gins(x86.ACLD, nil, nil) } else { // normal direction if q > 128 || (gc.Nacl && q >= 4) || (obj.Getgoos() == "plan9" && q >= 4) { gconreg(movptr, q, x86.REG_CX) gins(x86.AREP, nil, nil) // repeat gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ } else if q >= 4 { var oldx0 gc.Node var x0 gc.Node savex(x86.REG_X0, &x0, &oldx0, nil, gc.Types[gc.TFLOAT64]) p := gins(obj.ADUFFCOPY, nil, nil) p.To.Type = obj.TYPE_ADDR p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) // 64 blocks taking 14 bytes each // see ../../../../runtime/mkduff.go p.To.Offset = 14 * (64 - q/2) restx(&x0, &oldx0) if q%2 != 0 { gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ } } else if !gc.Nacl && c == 0 { // We don't need the MOVSQ side-effect of updating SI and DI, // and issuing a sequence of MOVQs directly is faster. nodsi.Op = gc.OINDREG noddi.Op = gc.OINDREG for q > 0 { gmove(&nodsi, &cx) // MOVQ x+(SI),CX gmove(&cx, &noddi) // MOVQ CX,x+(DI) nodsi.Xoffset += 8 noddi.Xoffset += 8 q-- } } else { for q > 0 { gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ q-- } } // copy the remaining c bytes if w < 4 || c <= 1 || (odst < osrc && osrc < odst+w) { for c > 0 { gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ c-- } } else if w < 8 || c <= 4 { nodsi.Op = gc.OINDREG noddi.Op = gc.OINDREG cx.Type = gc.Types[gc.TINT32] nodsi.Type = gc.Types[gc.TINT32] noddi.Type = gc.Types[gc.TINT32] if c > 4 { nodsi.Xoffset = 0 noddi.Xoffset = 0 gmove(&nodsi, &cx) gmove(&cx, &noddi) } nodsi.Xoffset = c - 4 noddi.Xoffset = c - 4 gmove(&nodsi, &cx) gmove(&cx, &noddi) } else { nodsi.Op = gc.OINDREG noddi.Op = gc.OINDREG cx.Type = gc.Types[gc.TINT64] nodsi.Type = gc.Types[gc.TINT64] noddi.Type = gc.Types[gc.TINT64] nodsi.Xoffset = c - 8 noddi.Xoffset = c - 8 gmove(&nodsi, &cx) gmove(&cx, &noddi) } } restx(&cx, &oldcx) }
// 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. package amd64 import ( "cmd/avail/obj" "cmd/avail/obj/x86" "cmd/compile/avail/gc" ) // no floating point in note handlers on Plan 9 var isPlan9 = obj.Getgoos() == "plan9" func defframe(ptxt *obj.Prog) { // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.ArgWidth(), int64(gc.Widthptr))) frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) ptxt.To.Offset = int64(frame) // insert code to zero ambiguously live variables // so that the garbage collector only sees initialized values // when it looks for pointers. p := ptxt hi := int64(0) lo := hi ax := uint32(0)
func Main() { defer hidePanic() goarch = obj.Getgoarch() Ctxt = obj.Linknew(Thearch.LinkArch) Ctxt.DiagFunc = Yyerror bstdout = bufio.NewWriter(os.Stdout) Ctxt.Bso = bstdout localpkg = mkpkg("") localpkg.Prefix = "\"\"" autopkg = mkpkg("") autopkg.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 itablinkpkg = mkpkg("go.itablink") itablinkpkg.Name = "go.itablink" itablinkpkg.Prefix = "go.itablink" // not go%2eitablink trackpkg = mkpkg("go.track") trackpkg.Name = "go.track" trackpkg.Prefix = "go.track" // not go%2etrack typepkg = mkpkg("type") typepkg.Name = "type" // pseudo-package used for map zero values mappkg = mkpkg("go.map") mappkg.Name = "go.map" mappkg.Prefix = "go.map" goroot = obj.Getgoroot() goos = obj.Getgoos() Nacl = goos == "nacl" if Nacl { flag_largemodel = true } flag.BoolVar(&compiling_runtime, "+", false, "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']) flag.StringVar(&localimport, "D", "", "set relative `path` for local imports") 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']) 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`") 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) flag.StringVar(&flag_installsuffix, "installsuffix", "", "set pkg directory `suffix`") obj.Flagcount("j", "debug runtime-initialized variables", &Debug['j']) obj.Flagcount("l", "disable inlining", &Debug['l']) flag.StringVar(&linkobj, "linkobj", "", "write linker-specific object to `file`") obj.Flagcount("live", "debug liveness analysis", &debuglive) obj.Flagcount("m", "print optimization decisions", &Debug['m']) flag.BoolVar(&flag_msan, "msan", false, "build code compatible with C/C++ memory sanitizer") flag.BoolVar(&newexport, "newexport", true, "use new export format") // TODO(gri) remove eventually (issue 15323) 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") obj.Flagcount("r", "debug generated wrappers", &Debug['r']) flag.BoolVar(&flag_race, "race", false, "enable race detector") obj.Flagcount("s", "warn about composite literals that can be simplified", &Debug['s']) flag.StringVar(&Ctxt.LineHist.TrimPathPrefix, "trimpath", "", "remove `prefix` from recorded source file paths") flag.BoolVar(&safemode, "u", false, "reject unsafe code") obj.Flagcount("v", "increase debug verbosity", &Debug['v']) obj.Flagcount("w", "debug type checking", &Debug['w']) flag.BoolVar(&use_writebarrier, "wb", true, "enable write barrier") obj.Flagcount("x", "debug lexer", &Debug['x']) var flag_shared bool var flag_dynlink bool if supportsDynlink(Thearch.LinkArch.Arch) { flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library") flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries") } if Thearch.LinkArch.Family == sys.AMD64 { flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model") } 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`") flag.BoolVar(&ssaEnabled, "ssa", true, "use SSA backend to generate code") obj.Flagparse(usage) Ctxt.Flag_shared = flag_dynlink || 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 { racepkg = mkpkg("runtime/race") racepkg.Name = "race" } if flag_msan { msanpkg = mkpkg("runtime/msan") msanpkg.Name = "msan" } if flag_race && flag_msan { log.Fatal("cannot use both -race and -msan") } else if flag_race || flag_msan { 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() Widthint = Thearch.LinkArch.IntSize Widthptr = Thearch.LinkArch.PtrSize Widthreg = Thearch.LinkArch.RegSize initUniverse() 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 finishUniverse() 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 { xtop[i] = 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 { xtop[i] = 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() typecheckslice(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 n.Func.Inl.Len() != 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) { for _, n := range list { 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 { checknowritebarrierrec() } // Phase 9: Check external declarations. for i, n := range externdcl { if n.Op == ONAME { externdcl[i] = typecheck(externdcl[i], Erv) } } if nerrors+nsavederrors != 0 { errorexit() } dumpobj() if asmhdr != "" { dumpasmhdr() } if nerrors+nsavederrors != 0 { errorexit() } Flusherrors() }
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 { 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: %s", file, p) 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() } } // process header lines 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") { importpkg.Safe = true break // ok to ignore rest } } // 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 if Debug_export != 0 { fmt.Printf("importing %s (%s)\n", path_, file) } imp.ReadByte() // skip \n after $$B Import(imp) default: Yyerror("no import in %q", path_) errorexit() } if safemode && !importpkg.Safe { Yyerror("cannot import unsafe package %q", importpkg.Path) } }