// Target is the package being compiled.
var Target *ir.Package
- // timing data for compiler phases
- var timings Timings
-
// Main parses flags and Go source files specified in the command-line
// arguments, type-checks the parsed Go package, compiles functions to machine
// code, and finally writes the compiled package definition to disk.
logopt.LogJsonOption(base.Flag.JSON)
}
+ ir.EscFmt = escFmt
IsIntrinsicCall = isIntrinsicCall
SSADumpInline = ssaDumpInline
-
- ssaDump = os.Getenv("GOSSAFUNC")
- ssaDir = os.Getenv("GOSSADIR")
- if ssaDump != "" {
- if strings.HasSuffix(ssaDump, "+") {
- ssaDump = ssaDump[:len(ssaDump)-1]
- ssaDumpStdout = true
- }
- spl := strings.Split(ssaDump, ":")
- if len(spl) > 1 {
- ssaDump = spl[0]
- ssaDumpCFG = spl[1]
- }
- }
+ initSSAEnv()
+ initSSATables()
Widthptr = thearch.LinkArch.PtrSize
Widthreg = thearch.LinkArch.RegSize
+ MaxWidth = thearch.MAXWIDTH
+ types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+ return typenamesym(t).Linksym()
+ }
Target = new(ir.Package)
- // initialize types package
- // (we need to do this to break dependencies that otherwise
- // would lead to import cycles)
- initializeTypesPackage()
-
- dclcontext = ir.PEXTERN
+ NeedFuncSym = makefuncsym
+ NeedITab = func(t, iface *types.Type) { itabname(t, iface) }
+ NeedRuntimeType = addsignat // TODO(rsc): typenamesym for lock?
autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
- timings.Start("fe", "loadsys")
- loadsys()
+ types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+ return typenamesym(t).Linksym()
+ }
+ TypecheckInit()
+ // Parse input.
timings.Start("fe", "parse")
lines := parseFiles(flag.Args())
cgoSymABIs()
timings.Stop()
timings.AddEvent(int64(lines), "lines")
- finishUniverse()
-
+ if base.Flag.G != 0 && base.Flag.G < 3 {
+ // can only parse generic code for now
+ base.ExitIfErrors()
+ return
+ }
+
recordPackageName()
- typecheckok = true
-
- // 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.
- //
- // We also defer type alias declarations until phase 2
- // to avoid cycles like #18640.
- // TODO(gri) Remove this again once we have a fix for #25838.
-
- // Don't use range--typecheck can add closures to Target.Decls.
- timings.Start("fe", "typecheck", "top1")
- for i := 0; i < len(Target.Decls); i++ {
- n := Target.Decls[i]
- if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).Left().Name().Alias()) {
- Target.Decls[i] = typecheck(n, ctxStmt)
- }
- }
-
- // Phase 2: Variable assignments.
- // To check interface assignments, depends on phase 1.
-
- // Don't use range--typecheck can add closures to Target.Decls.
- timings.Start("fe", "typecheck", "top2")
- for i := 0; i < len(Target.Decls); i++ {
- n := Target.Decls[i]
- if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).Left().Name().Alias() {
- Target.Decls[i] = typecheck(n, ctxStmt)
- }
- }
-
- // Phase 3: Type check function bodies.
- // Don't use range--typecheck can add closures to Target.Decls.
- timings.Start("fe", "typecheck", "func")
- var fcount int64
- for i := 0; i < len(Target.Decls); i++ {
- n := Target.Decls[i]
- if n.Op() == ir.ODCLFUNC {
- Curfn = n.(*ir.Func)
- decldepth = 1
- errorsBefore := base.Errors()
- typecheckslice(Curfn.Body().Slice(), ctxStmt)
- checkreturn(Curfn)
- if base.Errors() > errorsBefore {
- Curfn.PtrBody().Set(nil) // type errors; do not compile
- }
- // Now that we've checked whether n terminates,
- // we can eliminate some obviously dead code.
- deadcode(Curfn)
- fcount++
- }
- }
+ // Typecheck.
+ TypecheckPackage()
- // Phase 3.11: Check external declarations.
- // TODO(mdempsky): This should be handled when type checking their
- // corresponding ODCL nodes.
- timings.Start("fe", "typecheck", "externdcls")
- for i, n := range Target.Externs {
- if n.Op() == ir.ONAME {
- Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
- }
- }
-
- // Phase 3.14: With all user code type-checked, it's now safe to verify map keys
- // and unused dot imports.
- checkMapKeys()
+ // With all user code typechecked, it's now safe to verify unused dot imports.
checkDotImports()
base.ExitIfErrors()
- timings.AddEvent(fcount, "funcs")
-
+ // Build init task.
if initTask := fninit(); initTask != nil {
exportsym(initTask)
}
- // Phase 4: Decide how to capture closed variables.
- // This needs to run before escape analysis,
- // because variables captured by value do not escape.
- timings.Start("fe", "capturevars")
- for _, n := range Target.Decls {
- if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
- Curfn = n.(*ir.Func)
- capturevars(Curfn)
- }
- }
- capturevarscomplete = true
- Curfn = nil
- base.ExitIfErrors()
-
- // Phase 5: Inlining
+ // Inlining
timings.Start("fe", "inlining")
- if base.Debug.TypecheckInl != 0 {
- // Typecheck imported function bodies if Debug.l > 1,
- // otherwise lazily when used or re-exported.
- for _, n := range importlist {
- if n.Inl != nil {
- typecheckinl(n)
- }
- }
- base.ExitIfErrors()
- }
-
if base.Flag.LowerL != 0 {
- // Find functions that can be inlined and clone them before walk expands them.
- visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
- numfns := numNonClosures(list)
- for _, n := range list {
- if !recursive || numfns > 1 {
- // We allow inlining if there is no
- // recursion, or the recursion cycle is
- // across more than one function.
- caninl(n)
- } else {
- if base.Flag.LowerM > 1 {
- fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
- }
- }
- inlcalls(n)
- }
- })
+ InlinePackage()
}
+ // Devirtualize.
for _, n := range Target.Decls {
if n.Op() == ir.ODCLFUNC {
devirtualize(n.(*ir.Func))
}
Curfn = nil
- // Phase 6: Escape analysis.
+ // 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.
EnableNoWriteBarrierRecCheck()
}
- // Phase 7: Transform closure bodies to properly reference captured variables.
+ // 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.
timings.Start("fe", "xclosures")
for _, n := range Target.Decls {
- if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
- Curfn = n.(*ir.Func)
- transformclosure(Curfn)
+ if n.Op() == ir.ODCLFUNC {
+ n := n.(*ir.Func)
+ if n.Func().OClosure != nil {
+ Curfn = n
+ transformclosure(n)
+ }
}
}
Curfn = nil
peekitabs()
- // Phase 8: Compile top level functions.
+ // Compile top level functions.
// Don't use range--walk can add functions to Target.Decls.
timings.Start("be", "compilefuncs")
- fcount = 0
+ fcount := int64(0)
for i := 0; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if n.Op() == ir.ODCLFUNC {
dumpasmhdr()
}
- // Check whether any of the functions we have compiled have gigantic stack frames.
- sort.Slice(largeStackFrames, func(i, j int) bool {
- return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
- })
- for _, large := range largeStackFrames {
- if large.callee != 0 {
- base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
- } else {
- base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
- }
- }
+ CheckLargeStacks()
+ CheckFuncStack()
- if len(funcStack) != 0 {
- base.Fatalf("funcStack is non-empty: %v", len(funcStack))
- }
if len(compilequeue) != 0 {
base.Fatalf("%d uncompiled functions", len(compilequeue))
}
}
}
+ func CheckLargeStacks() {
+ // Check whether any of the functions we have compiled have gigantic stack frames.
+ sort.Slice(largeStackFrames, func(i, j int) bool {
+ return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
+ })
+ for _, large := range largeStackFrames {
+ if large.callee != 0 {
+ base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
+ } else {
+ base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
+ }
+ }
+ }
+
func cgoSymABIs() {
// The linker expects an ABI0 wrapper for all cgo-exported
// functions.
// any language version is supported.
var langWant lang
- // langSupported reports whether language version major.minor is
- // supported in a particular package.
- func langSupported(major, minor int, pkg *types.Pkg) bool {
+ // AllowsGoVersion reports whether a particular package
+ // is allowed to use Go version major.minor.
+ // We assume the imported packages have all been checked,
+ // so we only have to check the local package against the -lang flag.
+ func AllowsGoVersion(pkg *types.Pkg, major, minor int) bool {
if pkg == nil {
// TODO(mdempsky): Set Pkg for local types earlier.
pkg = types.LocalPkg
// Assume imported packages passed type-checking.
return true
}
-
if langWant.major == 0 && langWant.minor == 0 {
return true
}
return langWant.major > major || (langWant.major == major && langWant.minor >= minor)
}
+ func langSupported(major, minor int, pkg *types.Pkg) bool {
+ return AllowsGoVersion(pkg, major, minor)
+ }
+
// checkLang verifies that the -lang flag holds a valid value, and
// exits if not. It initializes data used by langSupported.
func checkLang() {
return lang{major: major, minor: minor}, nil
}
- func initializeTypesPackage() {
- types.Widthptr = Widthptr
- types.Dowidth = dowidth
- types.TypeLinkSym = func(t *types.Type) *obj.LSym {
- return typenamesym(t).Linksym()
+ // useNewABIWrapGen returns TRUE if the compiler should generate an
+ // ABI wrapper for the function 'f'.
+ func useABIWrapGen(f *ir.Func) bool {
+ if !base.Flag.ABIWrap {
+ return false
+ }
+
+ // Support limit option for bisecting.
+ if base.Flag.ABIWrapLimit == 1 {
+ return false
+ }
+ if base.Flag.ABIWrapLimit < 1 {
+ return true
+ }
+ base.Flag.ABIWrapLimit--
+ if base.Debug.ABIWrap != 0 && base.Flag.ABIWrapLimit == 1 {
+ fmt.Fprintf(os.Stderr, "=-= limit reached after new wrapper for %s\n",
+ f.LSym.Name)
}
- initUniverse()
+ return true
}
"fmt"
"go/constant"
"go/token"
+ "io"
"os"
"path/filepath"
"runtime"
"unicode/utf8"
"cmd/compile/internal/base"
+ "cmd/compile/internal/importer"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
"cmd/internal/objabi"
"cmd/internal/src"
)
// Each declaration in every *syntax.File is converted to a syntax tree
// and its root represented by *Node is appended to Target.Decls.
// Returns the total count of parsed lines.
-func parseFiles(filenames []string) uint {
+func parseFiles(filenames []string) (lines uint) {
noders := make([]*noder, 0, len(filenames))
// Limit the number of simultaneously open files.
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
sem <- struct{}{}
defer func() { <-sem }()
defer close(p.err)
- base := syntax.NewFileBase(filename)
+ fbase := syntax.NewFileBase(filename)
f, err := os.Open(filename)
if err != nil {
}
defer f.Close()
- p.file, _ = syntax.Parse(base, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error
+ mode := syntax.CheckBranches
+ if base.Flag.G != 0 {
+ mode |= syntax.AllowGenerics
+ }
+ p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
}(filename)
}
- var lines uint
+ // generic noding phase (using new typechecker)
+ if base.Flag.G != 0 {
+ // setup and syntax error reporting
+ nodersmap := make(map[string]*noder)
+ var files []*syntax.File
+ for _, p := range noders {
+ for e := range p.err {
+ p.errorAt(e.Pos, "%s", e.Msg)
+ }
+
+ nodersmap[p.file.Pos().RelFilename()] = p
+ files = append(files, p.file)
+ lines += p.file.EOF.Line()
+
+ }
+ if base.SyntaxErrors() != 0 {
+ base.ErrorExit()
+ }
+
+ // typechecking
+ conf := types2.Config{
+ InferFromConstraints: true,
+ IgnoreBranches: true, // parser already checked via syntax.CheckBranches mode
+ CompilerErrorMessages: true, // use error strings matching existing compiler errors
+ Error: func(err error) {
+ terr := err.(types2.Error)
+ if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
+ // types2 reports error clarifications via separate
+ // error messages which are indented with a tab.
+ // Ignore them to satisfy tools and tests that expect
+ // only one error in such cases.
+ // TODO(gri) Need to adjust error reporting in types2.
+ return
+ }
+ p := nodersmap[terr.Pos.RelFilename()]
+ base.ErrorfAt(p.makeXPos(terr.Pos), "%s", terr.Msg)
+ },
+ Importer: &gcimports{
+ packages: make(map[string]*types2.Package),
+ lookup: func(path string) (io.ReadCloser, error) {
+ file, ok := findpkg(path)
+ if !ok {
+ return nil, fmt.Errorf("can't find import: %q", path)
+ }
+ return os.Open(file)
+ },
+ },
+ }
+ info := types2.Info{
+ Types: make(map[syntax.Expr]types2.TypeAndValue),
+ Defs: make(map[*syntax.Name]types2.Object),
+ Uses: make(map[*syntax.Name]types2.Object),
+ Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
+ // expand as needed
+ }
+ conf.Check(base.Ctxt.Pkgpath, files, &info)
+ base.ExitIfErrors()
+ if base.Flag.G < 2 {
+ return
+ }
+
+ // noding
+ for _, p := range noders {
+ // errors have already been reported
+
+ p.typeInfo = &info
+ p.node()
+ lines += p.file.EOF.Line()
+ p.file = nil // release memory
+ base.ExitIfErrors()
+
+ // Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
+ testdclstack()
+ }
+
+ types.LocalPkg.Height = myheight
+ return
+ }
+
+ // traditional (non-generic) noding phase
for _, p := range noders {
for e := range p.err {
p.errorAt(e.Pos, "%s", e.Msg)
}
p.node()
- lines += p.file.Lines
+ lines += p.file.EOF.Line()
p.file = nil // release memory
-
if base.SyntaxErrors() != 0 {
base.ErrorExit()
}
+
// Always run testdclstack here, even when debug_dclstack is not set, as a sanity measure.
testdclstack()
}
}
types.LocalPkg.Height = myheight
+ return
+}
- return lines
+// Temporary import helper to get type2-based type-checking going.
+type gcimports struct {
+ packages map[string]*types2.Package
+ lookup func(path string) (io.ReadCloser, error)
+}
+
+func (m *gcimports) Import(path string) (*types2.Package, error) {
+ return m.ImportFrom(path, "" /* no vendoring */, 0)
+}
+
+func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
+ if mode != 0 {
+ panic("mode must be 0")
+ }
+ return importer.Import(m.packages, path, srcDir, m.lookup)
}
// makeSrcPosBase translates from a *syntax.PosBase to a *src.PosBase.
trackScopes bool
scopeVars []int
+ // typeInfo provides access to the type information computed by the new
+ // typechecker. It is only present if -G is set, and all noders point to
+ // the same types.Info. For now this is a local field, if need be we can
+ // make it global.
+ typeInfo *types2.Info
+
lastCloseScopePos syntax.Pos
}
+// For now we provide these basic accessors to get to type and object
+// information of expression nodes during noding. Eventually we will
+// attach this information directly to the syntax tree which should
+// simplify access and make it more efficient as well.
+
+// typ returns the type and value information for the given expression.
+func (p *noder) typ(x syntax.Expr) types2.TypeAndValue {
+ return p.typeInfo.Types[x]
+}
+
+// def returns the object for the given name in its declaration.
+func (p *noder) def(x *syntax.Name) types2.Object {
+ return p.typeInfo.Defs[x]
+}
+
+// use returns the object for the given name outside its declaration.
+func (p *noder) use(x *syntax.Name) types2.Object {
+ return p.typeInfo.Uses[x]
+}
+
+// sel returns the selection information for the given selector expression.
+func (p *noder) sel(x *syntax.SelectorExpr) *types2.Selection {
+ return p.typeInfo.Selections[x]
+}
+
func (p *noder) funcBody(fn *ir.Func, block *syntax.BlockStmt) {
oldScope := p.scope
p.scope = 0
}
func (p *noder) importDecl(imp *syntax.ImportDecl) {
- if imp.Path.Bad {
+ if imp.Path == nil || imp.Path.Bad {
return // avoid follow-on errors if there was a syntax error
}
}
func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node {
- names := p.declNames(decl.NameList)
+ names := p.declNames(ir.ONAME, decl.NameList)
typ := p.typeExprOrNil(decl.Type)
var exprs []ir.Node
p.checkUnused(pragma)
}
- names := p.declNames(decl.NameList)
+ names := p.declNames(ir.OLITERAL, decl.NameList)
typ := p.typeExprOrNil(decl.Type)
var values []ir.Node
nn := make([]ir.Node, 0, len(names))
for i, n := range names {
- n := n.(*ir.Name)
if i >= len(values) {
base.Errorf("missing value in const declaration")
break
if decl.Values == nil {
v = ir.DeepCopy(n.Pos(), v)
}
-
- n.SetOp(ir.OLITERAL)
declare(n, dclcontext)
n.Ntype = typ
}
func (p *noder) typeDecl(decl *syntax.TypeDecl) ir.Node {
- n := p.declName(decl.Name)
- n.SetOp(ir.OTYPE)
+ n := p.declName(ir.OTYPE, decl.Name)
declare(n, dclcontext)
// decl.Type may be nil but in that case we got a syntax error during parsing
return nod
}
- func (p *noder) declNames(names []*syntax.Name) []ir.Node {
- nodes := make([]ir.Node, 0, len(names))
+ func (p *noder) declNames(op ir.Op, names []*syntax.Name) []*ir.Name {
+ nodes := make([]*ir.Name, 0, len(names))
for _, name := range names {
- nodes = append(nodes, p.declName(name))
+ nodes = append(nodes, p.declName(op, name))
}
return nodes
}
- func (p *noder) declName(name *syntax.Name) *ir.Name {
- return ir.NewDeclNameAt(p.pos(name), p.name(name))
+ func (p *noder) declName(op ir.Op, name *syntax.Name) *ir.Name {
+ return ir.NewDeclNameAt(p.pos(name), op, p.name(name))
}
func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node {