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 main() { if obj.Getgoos() == "nacl" { resvd = append(resvd, x86.REG_BP, x86.REG_R15) } else if obj.Framepointer_enabled != 0 { resvd = append(resvd, x86.REG_BP) } gc.Thearch.Thechar = thechar gc.Thearch.Thestring = thestring gc.Thearch.Thelinkarch = thelinkarch gc.Thearch.Typedefs = typedefs gc.Thearch.REGSP = x86.REGSP gc.Thearch.REGCTXT = x86.REGCTXT gc.Thearch.REGCALLX = x86.REG_BX gc.Thearch.REGCALLX2 = x86.REG_AX gc.Thearch.REGRETURN = x86.REG_AX gc.Thearch.REGMIN = x86.REG_AX gc.Thearch.REGMAX = x86.REG_R15 gc.Thearch.FREGMIN = x86.REG_X0 gc.Thearch.FREGMAX = x86.REG_X15 gc.Thearch.MAXWIDTH = MAXWIDTH gc.Thearch.ReservedRegs = resvd gc.Thearch.AddIndex = addindex gc.Thearch.Betypeinit = betypeinit gc.Thearch.Cgen_bmul = cgen_bmul gc.Thearch.Cgen_hmul = cgen_hmul gc.Thearch.Cgen_shift = cgen_shift gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe gc.Thearch.Dodiv = dodiv gc.Thearch.Excise = excise gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove gc.Thearch.Linkarchinit = linkarchinit gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo gc.Thearch.Regtyp = regtyp gc.Thearch.Sameaddr = sameaddr gc.Thearch.Smallindir = smallindir gc.Thearch.Stackaddr = stackaddr gc.Thearch.Stackcopy = stackcopy gc.Thearch.Sudoaddable = sudoaddable gc.Thearch.Sudoclean = sudoclean gc.Thearch.Excludedregs = excludedregs gc.Thearch.RtoB = RtoB gc.Thearch.FtoB = FtoB gc.Thearch.BtoR = BtoR gc.Thearch.BtoF = BtoF gc.Thearch.Optoas = optoas gc.Thearch.Doregbits = doregbits gc.Thearch.Regnames = regnames gc.Main() gc.Exit(0) }
func (mode *BuildMode) Set(s string) error { goos := obj.Getgoos() goarch := obj.Getgoarch() badmode := func() error { return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch) } switch s { default: return fmt.Errorf("invalid buildmode: %q", s) case "exe": *mode = BuildmodeExe case "c-archive": switch goos { case "darwin", "linux": default: return badmode() } *mode = BuildmodeCArchive case "c-shared": if goarch != "amd64" && goarch != "arm" { return badmode() } *mode = BuildmodeCShared case "shared": if goos != "linux" || goarch != "amd64" { return badmode() } *mode = BuildmodeShared } return nil }
func assemble(file string) int { if outfile == "" { outfile = strings.TrimSuffix(filepath.Base(file), ".s") + "." + string(Thechar) } of, err := os.Create(outfile) if err != nil { Yyerror("%ca: cannot create %s", Thechar, outfile) errorexit() } obuf = *obj.Binitw(of) fmt.Fprintf(&obuf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(&obuf, "!\n") var i int for Pass = 1; Pass <= 2; Pass++ { pinit(file) for i = 0; i < len(Dlist); i++ { dodefine(Dlist[i]) } Yyparse() Cclean() if nerrors != 0 { return nerrors } } obj.Writeobjdirect(Ctxt, &obuf) obuf.Flush() return 0 }
// 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 mywhatsys() { goroot = obj.Getgoroot() goos = obj.Getgoos() goarch = obj.Getgoarch() if !strings.HasPrefix(goarch, Thestring) { log.Fatalf("cannot use %cc with GOARCH=%s", Thearch.Thechar, goarch) } }
func main() { log.SetFlags(0) log.SetPrefix("asm: ") GOARCH := obj.Getgoarch() architecture := arch.Set(GOARCH) if architecture == nil { log.Fatalf("unrecognized architecture %s", GOARCH) } flags.Parse() ctxt := obj.Linknew(architecture.LinkArch) if *flags.PrintOut { ctxt.Debugasm = 1 } ctxt.LineHist.TrimPathPrefix = *flags.TrimPath ctxt.Flag_dynlink = *flags.Dynlink ctxt.Flag_shared = *flags.Shared || *flags.Dynlink ctxt.Bso = bufio.NewWriter(os.Stdout) defer ctxt.Bso.Flush() // Create object file, write header. out, err := os.Create(*flags.OutputFile) if err != nil { log.Fatal(err) } defer bio.MustClose(out) buf := bufio.NewWriter(bio.MustWriter(out)) fmt.Fprintf(buf, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(buf, "!\n") lexer := lex.NewLexer(flag.Arg(0), ctxt) parser := asm.NewParser(ctxt, architecture, lexer) diag := false ctxt.DiagFunc = func(format string, args ...interface{}) { diag = true log.Printf(format, args...) } pList := obj.Linknewplist(ctxt) var ok bool pList.Firstpc, ok = parser.Parse() if ok { // reports errors to parser.Errorf obj.Writeobjdirect(ctxt, buf) } if !ok || diag { log.Printf("assembly of %s failed", flag.Arg(0)) os.Remove(*flags.OutputFile) os.Exit(1) } buf.Flush() }
func (mode *BuildMode) Set(s string) error { goos := obj.Getgoos() goarch := obj.Getgoarch() badmode := func() error { return fmt.Errorf("buildmode %s not supported on %s/%s", s, goos, goarch) } switch s { default: return fmt.Errorf("invalid buildmode: %q", s) case "exe": *mode = BuildmodeExe case "pie": switch goos { case "android", "linux": default: return badmode() } *mode = BuildmodePIE case "c-archive": switch goos { case "darwin", "linux": case "windows": switch goarch { case "amd64", "386": default: return badmode() } default: return badmode() } *mode = BuildmodeCArchive case "c-shared": switch goarch { case "386", "amd64", "arm", "arm64": default: return badmode() } *mode = BuildmodeCShared case "shared": switch goos { case "linux": switch goarch { case "386", "amd64", "arm", "arm64", "ppc64le", "s390x": default: return badmode() } default: return badmode() } *mode = BuildmodeShared } return nil }
func main() { log.SetFlags(0) log.SetPrefix("asm: ") GOARCH := obj.Getgoarch() architecture := arch.Set(GOARCH) if architecture == nil { log.Fatalf("asm: unrecognized architecture %s", GOARCH) } flags.Parse(architecture.Thechar) // Create object file, write header. fd, err := os.Create(*flags.OutputFile) if err != nil { log.Fatal(err) } ctxt := obj.Linknew(architecture.LinkArch) if *flags.PrintOut { ctxt.Debugasm = 1 } ctxt.LineHist.TrimPathPrefix = *flags.TrimPath ctxt.Flag_dynlink = *flags.Dynlink if *flags.Shared || *flags.Dynlink { ctxt.Flag_shared = 1 } ctxt.Bso = obj.Binitw(os.Stdout) defer ctxt.Bso.Flush() ctxt.Diag = log.Fatalf output := obj.Binitw(fd) fmt.Fprintf(output, "go object %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion()) fmt.Fprintf(output, "!\n") lexer := lex.NewLexer(flag.Arg(0), ctxt) parser := asm.NewParser(ctxt, architecture, lexer) pList := obj.Linknewplist(ctxt) var ok bool pList.Firstpc, ok = parser.Parse() if !ok { log.Printf("asm: assembly of %s failed", flag.Arg(0)) os.Remove(*flags.OutputFile) os.Exit(1) } obj.Writeobjdirect(ctxt, output) output.Flush() }
// 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) return c }
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 imp, err := obj.Bopenr(file) if err != nil { Yyerror("can't open import: %q: %v", path_, err) errorexit() } defer obj.Bterm(imp) if strings.HasSuffix(file, ".a") { if !skiptopkgdef(imp) { Yyerror("import %s: not a package file", file) errorexit() } } // check object header p := obj.Brdstr(imp, '\n', 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 int for { c = obj.Bgetc(imp) if c < 0 { break } if c == '$' { c = obj.Bgetc(imp) if c == '$' || c < 0 { break } } } // get character after $$ if c >= 0 { c = obj.Bgetc(imp) } switch c { case '\n': // old export format parse_import(imp, indent) case 'B': // new export format obj.Bgetc(imp) // 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 linknew(arch *LinkArch) *Link { ctxt := new(Link) ctxt.Hash = make(map[symVer]*LSym) ctxt.Arch = arch ctxt.Version = obj.HistVersion ctxt.Goroot = obj.Getgoroot() p := obj.Getgoarch() if p != arch.Name { log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) } var buf string buf, _ = os.Getwd() if buf == "" { buf = "/???" } buf = filepath.ToSlash(buf) 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" && ctxt.Arch.Thechar == '6' { // Android/x86 constant - offset from 0(FS) to our // TLS slot. Explained in src/runtime/cgo/gcc_android_*.c ctxt.Tlsoffset = 0x1d0 } else { ctxt.Tlsoffset = -1 * ctxt.Arch.Ptrsize } case obj.Hnacl: switch ctxt.Arch.Thechar { default: log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) case '5': ctxt.Tlsoffset = 0 case '6': ctxt.Tlsoffset = 0 case '8': 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.Thechar { default: log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) case '5': ctxt.Tlsoffset = 0 // dummy value, not needed case '6': ctxt.Tlsoffset = 0x8a0 case '7': ctxt.Tlsoffset = 0 // dummy value, not needed case '8': ctxt.Tlsoffset = 0x468 } } // On arm, record goarm. if ctxt.Arch.Thechar == '5' { ctxt.Goarm = obj.Getgoarm() } return ctxt }
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) }
} // 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 Ldmain() { Ctxt = linknew(Thelinkarch) Ctxt.Thechar = int32(Thearch.Thechar) Ctxt.Thestring = Thestring Ctxt.Diag = Diag Ctxt.Bso = &Bso Bso = *obj.Binitw(os.Stdout) 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 Thearch.Thechar == '6' && 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.Flagcount("W", "disassemble input", &Debug['W']) 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']) 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.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) flag.BoolVar(&Linkshared, "linkshared", false, "link against installed Go shared libraries") 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 Thearch.Thechar == '5' || Thearch.Thechar == '6' { 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) // Clumsy hack to preserve old two-argument -X name val syntax for old scripts. // Rewrite that syntax into new syntax -X name=val. // TODO(rsc): Delete this hack in Go 1.6 or later. var args []string for i := 0; i < len(os.Args); i++ { arg := os.Args[i] if (arg == "-X" || arg == "--X") && i+2 < len(os.Args) && !strings.Contains(os.Args[i+1], "=") { fmt.Fprintf(os.Stderr, "link: warning: option %s %s %s may not work in future releases; use %s %s=%s\n", arg, os.Args[i+1], os.Args[i+2], arg, os.Args[i+1], os.Args[i+2]) args = append(args, arg) args = append(args, os.Args[i+1]+"="+os.Args[i+2]) i += 2 continue } if (strings.HasPrefix(arg, "-X=") || strings.HasPrefix(arg, "--X=")) && i+1 < len(os.Args) && strings.Count(arg, "=") == 1 { fmt.Fprintf(os.Stderr, "link: warning: option %s %s may not work in future releases; use %s=%s\n", arg, os.Args[i+1], arg, os.Args[i+1]) args = append(args, arg+"="+os.Args[i+1]) i++ continue } args = append(args, arg) } os.Args = args 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)) } if HEADTYPE == obj.Hhaiku { Linkmode = LinkExternal } 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() if Thearch.Thechar == '5' { // mark some functions that are only referenced after linker code editing if Ctxt.Goarm == 5 { mark(Linkrlookup(Ctxt, "_sfloat", 0)) } mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) } checkgo() checkstrdata() deadcode() 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() doweak() 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", Ctxt.Nsymbol) fmt.Fprintf(&Bso, "%d liveness data\n", liveness) } Bso.Flush() errorexit() }
func importfile(f *Val, line int) { if f.Ctype != CTSTR { Yyerror("import statement not a string") fakeimport() return } if len(f.U.Sval) == 0 { Yyerror("import path is empty") fakeimport() return } if isbadimport(f.U.Sval) { fakeimport() 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 f.U.Sval == "main" { Yyerror("cannot import \"main\"") errorexit() } if myimportpath != "" && f.U.Sval == myimportpath { Yyerror("import %q while compiling that package (import cycle)", f.U.Sval) errorexit() } if f.U.Sval == "unsafe" { if safemode != 0 { Yyerror("cannot import package unsafe") errorexit() } importpkg = mkpkg(f.U.Sval) cannedimports("unsafe.6", unsafeimport) imported_unsafe = 1 return } path_ := f.U.Sval if islocalname(path_) { if path_[0] == '/' { Yyerror("import path cannot be absolute path") fakeimport() return } prefix := Ctxt.Pathname if localimport != "" { prefix = localimport } cleanbuf := prefix cleanbuf += "/" cleanbuf += path_ cleanbuf = path.Clean(cleanbuf) path_ = cleanbuf if isbadimport(path_) { fakeimport() return } } file, found := findpkg(path_) if !found { Yyerror("can't find import: %q", f.U.Sval) errorexit() } importpkg = mkpkg(path_) // If we already saw that package, feed a dummy statement // to the lexer to avoid parsing export data twice. if importpkg.Imported != 0 { tag := "" if importpkg.Safe { tag = "safe" } p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) cannedimports(file, p) return } importpkg.Imported = 1 var err error var imp *obj.Biobuf imp, err = obj.Bopenr(file) if err != nil { Yyerror("can't open import: %q: %v", f.U.Sval, err) errorexit() } if strings.HasSuffix(file, ".a") { if !skiptopkgdef(imp) { Yyerror("import %s: not a package file", file) errorexit() } } // check object header p := obj.Brdstr(imp, '\n', 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. linehist(file[len(file)-len(path_)-2:], -1, 1) // acts as #pragma lib /* * position the input right * after $$ and return */ pushedio = curio curio.bin = imp curio.peekc = 0 curio.peekc1 = 0 curio.infile = file curio.nlsemi = 0 typecheckok = 1 var c int32 for { c = int32(getc()) if c == EOF { break } if c != '$' { continue } c = int32(getc()) if c == EOF { break } if c != '$' { continue } return } Yyerror("no import in %q", f.U.Sval) unimportfile() }
func dumpobj() { var err error bout, err = obj.Bopenw(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive != 0 { obj.Bwritestring(bout, "!<arch>\n") arhdr = [ArhdrSize]byte{} obj.Bwrite(bout, arhdr[:]) startobj = obj.Boffset(bout) } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) dumpexport() if writearchive != 0 { obj.Bflush(bout) size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) obj.Bwrite(bout, arhdr[:]) obj.Bflush(bout) obj.Bseek(bout, startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} obj.Bwrite(bout, arhdr[:]) startobj = obj.Boffset(bout) fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } if pragcgobuf != "" { if writearchive != 0 { // write empty export section; must be before cgo section fmt.Fprintf(bout, "\n$$\n\n$$\n\n") } fmt.Fprintf(bout, "\n$$ // cgo\n") fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) } fmt.Fprintf(bout, "\n!\n") var externs *NodeList if externdcl != nil { externs = externdcl.End } dumpglobls() dumptypestructs() // Dump extra globals. tmp := externdcl if externs != nil { externdcl = externs.Next } dumpglobls() externdcl = tmp zero := Pkglookup("zerovalue", Runtimepkg) ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) dumpdata() obj.Writeobjdirect(Ctxt, bout) if writearchive != 0 { obj.Bflush(bout) size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) name := fmt.Sprintf("_go_.%c", Thearch.Thechar) formathdr(arhdr[:], name, size) obj.Bwrite(bout, arhdr[:]) } obj.Bterm(bout) }
// Main is the main entry point for the linker code. func Main() { ctxt := linknew(SysArch) ctxt.Bso = bufio.NewWriter(os.Stdout) nerrors = 0 HEADTYPE = -1 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) } } // TODO(matloob): define these above and then check flag values here if SysArch.Family == sys.AMD64 && obj.Getgoos() == "plan9" { flag.BoolVar(&Flag8, "8", false, "use 64-bit addresses in symbol table") } obj.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) obj.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) obj.Flagfn1("H", "set header `type`", setheadtype) obj.Flagfn0("V", "print version and exit", doversion) obj.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) }) obj.Flagcount("v", "print link trace", &ctxt.Debugvlog) obj.Flagfn1("linkmode", "set link `mode` (internal, external, auto)", setlinkmode) var flagShared bool if SysArch.InFamily(sys.ARM, sys.AMD64) { flag.BoolVar(&flagShared, "shared", false, "generate shared object (implies -linkmode external)") } obj.Flagparse(usage) startProfile() if flagShared { 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 *flagOutfile == "" { *flagOutfile = "a.out" if HEADTYPE == obj.Hwindows { *flagOutfile += ".exe" } } interpreter = *flagInterpreter libinit(ctxt) // creates outfile if HEADTYPE == -1 { HEADTYPE = int32(headtype(goos)) } ctxt.Headtype = int(HEADTYPE) if headstring == "" { headstring = Headstr(int(HEADTYPE)) } Thearch.Archinit(ctxt) if *FlagLinkshared && !Iself { Exitf("-linkshared can only be used on elf systems") } if ctxt.Debugvlog != 0 { ctxt.Logf("HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(*FlagTextAddr), uint64(*FlagDataAddr), uint32(*FlagRound)) } 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", "") } ctxt.loadlib() ctxt.checkstrdata() deadcode(ctxt) fieldtrack(ctxt) ctxt.callgraph() ctxt.doelf() if HEADTYPE == obj.Hdarwin { ctxt.domacho() } ctxt.dostkcheck() if HEADTYPE == obj.Hwindows { ctxt.dope() } ctxt.addexport() Thearch.Gentext(ctxt) // trampolines, call stubs, etc. ctxt.textbuildid() ctxt.textaddress() ctxt.pclntab() ctxt.findfunctab() ctxt.symtab() ctxt.dodata() ctxt.address() ctxt.reloc() Thearch.Asmb(ctxt) ctxt.undef() ctxt.hostlink() ctxt.archive() if ctxt.Debugvlog != 0 { ctxt.Logf("%5.2f cpu time\n", obj.Cputime()) ctxt.Logf("%d symbols\n", len(ctxt.Allsym)) ctxt.Logf("%d liveness data\n", liveness) } ctxt.Bso.Flush() errorexit() }
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.Diag = 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 weaktypepkg = mkpkg("go.weak.type") weaktypepkg.Name = "go.weak.type" weaktypepkg.Prefix = "go.weak.type" // not go%2eweak%2etype 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", "path: set relative path for local imports", &localimport) obj.Flagcount("E", "debug symbol export", &Debug['E']) obj.Flagfn1("I", "dir: add dir 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", "file: write assembly header to named file", &asmhdr) obj.Flagcount("complete", "compiling complete package (no C or assembly)", &pure_go) obj.Flagstr("d", "list: 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.Flagstr("installsuffix", "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("nolocalimports", "reject local (relative) imports", &nolocalimports) obj.Flagstr("o", "obj: set output file", &outfile) obj.Flagstr("p", "path: 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", "prefix: remove prefix from recorded source file paths", &Ctxt.Trimpath) 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 if Thearch.Thechar == '6' { obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel) obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared) } obj.Flagstr("cpuprofile", "file: write cpu profile to file", &cpuprofile) obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile) obj.Flagparse(usage) Ctxt.Flag_shared = int32(flag_shared) 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" } // parse -d argument if debugstr != "" { var j int f := strings.Split(debugstr, ",") for i := range f { if f[i] == "" { continue } for j = 0; j < len(debugtab); j++ { if debugtab[j].name == f[i] { if debugtab[j].val != nil { *debugtab[j].val = 1 } break } } if j >= len(debugtab) { log.Fatalf("unknown debug information -d '%s'\n", f[i]) } } } // 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 { Fatal("betypeinit failed") } lexinit() typeinit() lexinit1() // TODO(rsc): Restore yytinit? blockgen = 1 dclcontext = PEXTERN nerrors = 0 lexlineno = 1 for _, infile = range flag.Args() { linehist(infile, 0, 0) curio.infile = infile var err error curio.bin, err = obj.Bopenr(infile) if err != nil { fmt.Printf("open %s: %v\n", infile, err) errorexit() } curio.peekc = 0 curio.peekc1 = 0 curio.nlsemi = 0 curio.eofnl = 0 curio.last = 0 // Skip initial BOM if present. if obj.Bgetrune(curio.bin) != obj.BOM { obj.Bungetrune(curio.bin) } block = 1 iota_ = -1000000 imported_unsafe = 0 yyparse() if nsyntaxerrors != 0 { errorexit() } linehist("<pop>", 0, 0) if curio.bin != nil { obj.Bterm(curio.bin) } } testdclstack() mkpackage(localpkg.Name) // final import not used checks lexfini() typecheckok = 1 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() for l := xtop; l != nil; l = l.Next { if l.N.Op != ODCL && l.N.Op != OAS { typecheck(&l.N, Etop) } } // Phase 2: Variable assignments. // To check interface assignments, depends on phase 1. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCL || l.N.Op == OAS { typecheck(&l.N, Etop) } } resumecheckwidth() // Phase 3: Type check function bodies. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC || l.N.Op == OCLOSURE { Curfn = l.N decldepth = 1 saveerrors() typechecklist(l.N.Nbody, Etop) checkreturn(l.N) if nerrors != 0 { l.N.Nbody = 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 l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Closure != nil { Curfn = l.N capturevars(l.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 l := importlist; l != nil; l = l.Next { if l.N.Func.Inl != nil { saveerrors() typecheckinl(l.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 *NodeList, recursive bool) { for l := list; l != nil; l = l.Next { if l.N.Op == ODCLFUNC { caninl(l.N) inlcalls(l.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. escapes(xtop) // Escape analysis moved escaped values off stack. // Move large values off stack too. movelarge(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 l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Closure != nil { Curfn = l.N transformclosure(l.N) } } Curfn = nil // Phase 8: Compile top level functions. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC { funccompile(l.N) } } if nsavederrors+nerrors == 0 { fninit(xtop) } // Phase 9: Check external declarations. for l := externdcl; l != nil; l = l.Next { if l.N.Op == ONAME { typecheck(&l.N, 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 (textual format): not supported anymore // $$B\n (binary 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': Yyerror("cannot import %s: old export format no longer supported (recompile library)", path_) case 'B': 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) } }
// 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[:] c.gpRegMask = gpRegMaskAMD64 c.fpRegMask = fpRegMaskAMD64 c.FPReg = framepointerRegAMD64 c.hasGReg = false case "amd64p32": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlockAMD64 c.lowerValue = rewriteValueAMD64 c.registers = registersAMD64[:] c.gpRegMask = gpRegMaskAMD64 c.fpRegMask = fpRegMaskAMD64 c.FPReg = framepointerRegAMD64 c.hasGReg = false c.noDuffDevice = true case "386": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlock386 c.lowerValue = rewriteValue386 c.registers = registers386[:] c.gpRegMask = gpRegMask386 c.fpRegMask = fpRegMask386 c.FPReg = framepointerReg386 c.hasGReg = false case "arm": c.IntSize = 4 c.PtrSize = 4 c.lowerBlock = rewriteBlockARM c.lowerValue = rewriteValueARM c.registers = registersARM[:] c.gpRegMask = gpRegMaskARM c.fpRegMask = fpRegMaskARM c.FPReg = framepointerRegARM c.hasGReg = true case "arm64": c.IntSize = 8 c.PtrSize = 8 c.lowerBlock = rewriteBlockARM64 c.lowerValue = rewriteValueARM64 c.registers = registersARM64[:] c.gpRegMask = gpRegMaskARM64 c.fpRegMask = fpRegMaskARM64 c.FPReg = framepointerRegARM64 c.hasGReg = true c.noDuffDevice = obj.Getgoos() == "darwin" // darwin linker cannot handle BR26 reloc with non-zero addend case "ppc64le": c.IntSize = 8 c.PtrSize = 8 c.lowerBlock = rewriteBlockPPC64 c.lowerValue = rewriteValuePPC64 c.registers = registersPPC64[:] c.gpRegMask = gpRegMaskPPC64 c.fpRegMask = fpRegMaskPPC64 c.FPReg = framepointerRegPPC64 c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy) c.NeedsFpScratch = true c.hasGReg = true default: fe.Unimplementedf(0, "arch %s not implemented", arch) } c.ctxt = ctxt c.optimize = optimize c.nacl = obj.Getgoos() == "nacl" // Don't use Duff's device on Plan 9 AMD64, because floating // point operations are not allowed in note handler. if obj.Getgoos() == "plan9" && arch == "amd64" { c.noDuffDevice = true } if c.nacl { c.noDuffDevice = true // Don't use Duff's device on NaCl // ARM assembler rewrites DIV/MOD to runtime calls, which // clobber R12 on nacl opcodeTable[OpARMDIV].reg.clobbers |= 1 << 12 // R12 opcodeTable[OpARMDIVU].reg.clobbers |= 1 << 12 // R12 opcodeTable[OpARMMOD].reg.clobbers |= 1 << 12 // R12 opcodeTable[OpARMMODU].reg.clobbers |= 1 << 12 // R12 } // 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 Main() { if obj.Getgoos() == "nacl" { resvd = append(resvd, x86.REG_BP, x86.REG_R15) } else if obj.Framepointer_enabled != 0 { resvd = append(resvd, x86.REG_BP) } gc.Thearch.Thechar = '6' gc.Thearch.Thestring = "amd64" gc.Thearch.Thelinkarch = &x86.Linkamd64 if obj.Getgoarch() == "amd64p32" { gc.Thearch.Thestring = "amd64p32" gc.Thearch.Thelinkarch = &x86.Linkamd64p32 } gc.Thearch.REGSP = x86.REGSP gc.Thearch.REGCTXT = x86.REGCTXT gc.Thearch.REGCALLX = x86.REG_BX gc.Thearch.REGCALLX2 = x86.REG_AX gc.Thearch.REGRETURN = x86.REG_AX gc.Thearch.REGMIN = x86.REG_AX gc.Thearch.REGMAX = x86.REG_R15 gc.Thearch.FREGMIN = x86.REG_X0 gc.Thearch.FREGMAX = x86.REG_X15 gc.Thearch.MAXWIDTH = 1 << 50 gc.Thearch.ReservedRegs = resvd gc.Thearch.AddIndex = addindex gc.Thearch.Betypeinit = betypeinit gc.Thearch.Cgen_bmul = cgen_bmul gc.Thearch.Cgen_hmul = cgen_hmul gc.Thearch.Cgen_shift = cgen_shift gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe gc.Thearch.Dodiv = dodiv gc.Thearch.Excise = excise gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins gc.Thearch.Ginsboolval = ginsboolval gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo gc.Thearch.Regtyp = regtyp gc.Thearch.Sameaddr = sameaddr gc.Thearch.Smallindir = smallindir gc.Thearch.Stackaddr = stackaddr gc.Thearch.Blockcopy = blockcopy gc.Thearch.Sudoaddable = sudoaddable gc.Thearch.Sudoclean = sudoclean gc.Thearch.Excludedregs = excludedregs gc.Thearch.RtoB = RtoB gc.Thearch.FtoB = FtoB gc.Thearch.BtoR = BtoR gc.Thearch.BtoF = BtoF gc.Thearch.Optoas = optoas gc.Thearch.Doregbits = doregbits gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = ssaMarkMoves gc.Thearch.SSAGenValue = ssaGenValue gc.Thearch.SSAGenBlock = ssaGenBlock gc.Main() gc.Exit(0) }
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 dumpobj1(outfile string, mode int) { var err error bout, err = bio.Create(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive { bout.WriteString("!<arch>\n") arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = bout.Offset() } printheader := func() { fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) if buildid != "" { fmt.Fprintf(bout, "build id %q\n", buildid) } if localpkg.Name == "main" { fmt.Fprintf(bout, "main\n") } if safemode { fmt.Fprintf(bout, "safe\n") } else { fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe" } fmt.Fprintf(bout, "\n") // header ends with blank line } printheader() if mode&modeCompilerObj != 0 { dumpexport() } if writearchive { bout.Flush() size := bout.Offset() - startobj if size&1 != 0 { bout.WriteByte(0) } bout.Seek(startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() bout.Seek(startobj+size+(size&1), 0) } if mode&modeLinkerObj == 0 { bout.Close() return } if writearchive { // start object file arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = bout.Offset() printheader() } if pragcgobuf != "" { if writearchive { // write empty export section; must be before cgo section fmt.Fprintf(bout, "\n$$\n\n$$\n\n") } fmt.Fprintf(bout, "\n$$ // cgo\n") fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) } fmt.Fprintf(bout, "\n!\n") externs := len(externdcl) dumpglobls() dumptypestructs() // Dump extra globals. tmp := externdcl if externdcl != nil { externdcl = externdcl[externs:] } dumpglobls() externdcl = tmp if zerosize > 0 { zero := Pkglookup("zero", mappkg) ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) } dumpdata() obj.Writeobjdirect(Ctxt, bout.Writer) if writearchive { bout.Flush() size := bout.Offset() - startobj if size&1 != 0 { bout.WriteByte(0) } bout.Seek(startobj-ArhdrSize, 0) formathdr(arhdr[:], "_go_.o", size) bout.Write(arhdr[:]) } bout.Close() }
// 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/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/x86" ) // 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 dumpobj() { var err error bout, err = obj.Bopenw(outfile) if err != nil { Flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } startobj := int64(0) var arhdr [ArhdrSize]byte if writearchive != 0 { obj.Bwritestring(bout, "!<arch>\n") arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = obj.Boffset(bout) } fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) dumpexport() if writearchive != 0 { bout.Flush() size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "__.PKGDEF", size) bout.Write(arhdr[:]) bout.Flush() obj.Bseek(bout, startobj+size+(size&1), 0) arhdr = [ArhdrSize]byte{} bout.Write(arhdr[:]) startobj = obj.Boffset(bout) fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) } if pragcgobuf != "" { if writearchive != 0 { // write empty export section; must be before cgo section fmt.Fprintf(bout, "\n$$\n\n$$\n\n") } fmt.Fprintf(bout, "\n$$ // cgo\n") fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) } fmt.Fprintf(bout, "\n!\n") externs := len(externdcl) dumpglobls() dumptypestructs() // Dump extra globals. tmp := externdcl if externdcl != nil { externdcl = externdcl[externs:] } dumpglobls() externdcl = tmp dumpdata() obj.Writeobjdirect(Ctxt, bout) if writearchive != 0 { bout.Flush() size := obj.Boffset(bout) - startobj if size&1 != 0 { obj.Bputc(bout, 0) } obj.Bseek(bout, startobj-ArhdrSize, 0) formathdr(arhdr[:], "_go_.o", size) bout.Write(arhdr[:]) } obj.Bterm(bout) }
// TODO(gri) line argument doesn't appear to be used func importfile(f *Val, line int) { if _, ok := f.U.(string); !ok { Yyerror("import statement not a string") fakeimport() return } if len(f.U.(string)) == 0 { Yyerror("import path is empty") fakeimport() return } if isbadimport(f.U.(string)) { fakeimport() 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 f.U.(string) == "main" { Yyerror("cannot import \"main\"") errorexit() } if myimportpath != "" && f.U.(string) == myimportpath { Yyerror("import %q while compiling that package (import cycle)", f.U.(string)) errorexit() } path_ := f.U.(string) if mapped, ok := importMap[path_]; ok { path_ = mapped } if path_ == "unsafe" { if safemode != 0 { Yyerror("cannot import package unsafe") errorexit() } importpkg = mkpkg(f.U.(string)) cannedimports("unsafe.o", unsafeimport) imported_unsafe = true return } if islocalname(path_) { if path_[0] == '/' { Yyerror("import path cannot be absolute path") fakeimport() return } prefix := Ctxt.Pathname if localimport != "" { prefix = localimport } cleanbuf := prefix cleanbuf += "/" cleanbuf += path_ cleanbuf = path.Clean(cleanbuf) path_ = cleanbuf if isbadimport(path_) { fakeimport() return } } file, found := findpkg(path_) if !found { Yyerror("can't find import: %q", f.U.(string)) errorexit() } importpkg = mkpkg(path_) // If we already saw that package, feed a dummy statement // to the lexer to avoid parsing export data twice. if importpkg.Imported { tag := "" if importpkg.Safe { tag = "safe" } p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) cannedimports(file, p) return } importpkg.Imported = true var err error var imp *obj.Biobuf imp, err = obj.Bopenr(file) if err != nil { Yyerror("can't open import: %q: %v", f.U.(string), err) errorexit() } if strings.HasSuffix(file, ".a") { if !skiptopkgdef(imp) { Yyerror("import %s: not a package file", file) errorexit() } } // check object header p := obj.Brdstr(imp, '\n', 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 int for { c = obj.Bgetc(imp) if c < 0 { break } if c == '$' { c = obj.Bgetc(imp) if c == '$' || c < 0 { break } } } // get character after $$ if c >= 0 { c = obj.Bgetc(imp) } switch c { case '\n': // old export format pushedio = curio curio.bin = imp curio.peekc = 0 curio.peekc1 = 0 curio.infile = file curio.nlsemi = false typecheckok = true push_parser() case 'B': // new export format obj.Bgetc(imp) // skip \n after $$B Import(imp) // continue as if the package was imported before (see above) tag := "" if importpkg.Safe { tag = "safe" } p := fmt.Sprintf("package %s %s\n$$\n", importpkg.Name, tag) cannedimports(file, p) // Reset incannedimport flag (we are not truly in a // canned import) - this will cause importpkg.Direct to // be set via parser.import_package (was issue #13977). // // TODO(gri) Remove this global variable and convoluted // code in the process of streamlining the import code. incannedimport = 0 default: Yyerror("no import in %q", f.U.(string)) } }
func linknew(arch *LinkArch) *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.Thechar { case '6': // Android/amd64 constant - offset from 0(FS) to our TLS slot. // Explained in src/runtime/cgo/gcc_android_*.c ctxt.Tlsoffset = 0x1d0 case '8': // 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.Thechar { default: log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) case '5': ctxt.Tlsoffset = 0 case '6': ctxt.Tlsoffset = 0 case '8': 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.Thechar { default: log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) case '5': ctxt.Tlsoffset = 0 // dummy value, not needed case '6': ctxt.Tlsoffset = 0x8a0 case '7': ctxt.Tlsoffset = 0 // dummy value, not needed case '8': ctxt.Tlsoffset = 0x468 } } // On arm, record goarm. if ctxt.Arch.Thechar == '5' { ctxt.Goarm = obj.Getgoarm() } return ctxt }
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 weaktypepkg = mkpkg("go.weak.type") weaktypepkg.Name = "go.weak.type" weaktypepkg.Prefix = "go.weak.type" // not go%2eweak%2etype 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) obj.Flagparse(usage) if flag_dynlink { flag_shared = 1 } Ctxt.Flag_shared = int32(flag_shared) Ctxt.Flag_dynlink = flag_dynlink 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("can not 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 } } } 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 const BOM = 0xFEFF for _, infile = range flag.Args() { if trace && Debug['x'] != 0 { fmt.Printf("--- %s ---\n", infile) } linehistpush(infile) curio.infile = infile var err error curio.bin, err = obj.Bopenr(infile) if err != nil { fmt.Printf("open %s: %v\n", infile, err) errorexit() } curio.peekc = 0 curio.peekc1 = 0 curio.nlsemi = false curio.eofnl = false curio.last = 0 // Skip initial BOM if present. if obj.Bgetrune(curio.bin) != BOM { obj.Bungetrune(curio.bin) } block = 1 iota_ = -1000000 imported_unsafe = false parse_file() if nsyntaxerrors != 0 { errorexit() } linehistpop() if curio.bin != nil { obj.Bterm(curio.bin) } } 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() for l := xtop; l != nil; l = l.Next { if l.N.Op != ODCL && l.N.Op != OAS && l.N.Op != OAS2 { typecheck(&l.N, Etop) } } // Phase 2: Variable assignments. // To check interface assignments, depends on phase 1. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCL || l.N.Op == OAS || l.N.Op == OAS2 { typecheck(&l.N, Etop) } } resumecheckwidth() // Phase 3: Type check function bodies. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC || l.N.Op == OCLOSURE { Curfn = l.N decldepth = 1 saveerrors() typechecklist(l.N.Nbody, Etop) checkreturn(l.N) if nerrors != 0 { l.N.Nbody = 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 l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Func.Closure != nil { Curfn = l.N capturevars(l.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 != nil { 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 l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC && l.N.Func.Closure != nil { Curfn = l.N transformclosure(l.N) } } Curfn = nil // Phase 8: Compile top level functions. for l := xtop; l != nil; l = l.Next { if l.N.Op == ODCLFUNC { funccompile(l.N) } } 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() }