Exemplo n.º 1
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
}
Exemplo n.º 2
0
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
}
Exemplo n.º 3
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()
}
Exemplo n.º 4
0
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
}
Exemplo n.º 5
0
		}

		// 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)
Exemplo n.º 6
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)
}
Exemplo n.º 7
0
// 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)
Exemplo n.º 8
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()
}
Exemplo n.º 9
0
func importfile(f *Val, indent []byte) {
	if importpkg != nil {
		Fatalf("importpkg not nil")
	}

	path_, ok := f.U.(string)
	if !ok {
		Yyerror("import statement not a string")
		return
	}

	if len(path_) == 0 {
		Yyerror("import path is empty")
		return
	}

	if isbadimport(path_) {
		return
	}

	// The package name main is no longer reserved,
	// but we reserve the import path "main" to identify
	// the main package, just as we reserve the import
	// path "math" to identify the standard math package.
	if path_ == "main" {
		Yyerror("cannot import \"main\"")
		errorexit()
	}

	if myimportpath != "" && path_ == myimportpath {
		Yyerror("import %q while compiling that package (import cycle)", path_)
		errorexit()
	}

	if mapped, ok := importMap[path_]; ok {
		path_ = mapped
	}

	if path_ == "unsafe" {
		if safemode {
			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)
	}
}