diff --git a/compiler/compiler.go b/compiler/compiler.go index faa5038a..e25cc673 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -84,12 +84,13 @@ type compilerContext struct { // importantly with a newly created LLVM context and module. func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool) *compilerContext { c := &compilerContext{ - Config: config, - DumpSSA: dumpSSA, - difiles: make(map[string]llvm.Metadata), - ditypes: make(map[types.Type]llvm.Metadata), - machine: machine, - targetData: machine.CreateTargetData(), + Config: config, + DumpSSA: dumpSSA, + difiles: make(map[string]llvm.Metadata), + ditypes: make(map[types.Type]llvm.Metadata), + machine: machine, + targetData: machine.CreateTargetData(), + astComments: map[string]*ast.CommentGroup{}, } c.ctx = llvm.NewContext() @@ -251,12 +252,6 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config c.program.Build() c.runtimePkg = c.program.ImportedPackage("runtime").Pkg - // Run a simple dead code elimination pass. - functions, err := c.simpleDCE(lprogram) - if err != nil { - return llvm.Module{}, []error{err} - } - // Initialize debug information. if c.Debug { c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ @@ -268,27 +263,22 @@ func CompileProgram(lprogram *loader.Program, machine llvm.TargetMachine, config }) } - c.loadASTComments(lprogram) + for _, pkg := range lprogram.Sorted() { + c.loadASTComments(pkg) + } // Predeclare the runtime.alloc function, which is used by the wordpack // functionality. c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function)) - // Add definitions to declarations. + // Define all functions. var initFuncs []llvm.Value irbuilder := c.ctx.NewBuilder() defer irbuilder.Dispose() - for _, f := range functions { - if f.Synthetic == "package initializer" { - initFuncs = append(initFuncs, c.getFunction(f)) - } - if f.Blocks == nil { - continue // external function - } - - // Create the function definition. - b := newBuilder(c, irbuilder, f) - b.createFunction() + for _, pkg := range lprogram.Sorted() { + ssaPkg := c.program.Package(pkg.Pkg) + c.createPackage(irbuilder, ssaPkg) + initFuncs = append(initFuncs, c.getFunction(ssaPkg.Members["init"].(*ssa.Function))) } // After all packages are imported, add a synthetic initializer function @@ -341,38 +331,10 @@ func CompilePackage(moduleName string, pkg *loader.Package, machine llvm.TargetM ssaPkg := pkg.LoadSSA() ssaPkg.Build() - // Sort by position, so that the order of the functions in the IR matches - // the order of functions in the source file. This is useful for testing, - // for example. - var members []string - for name := range ssaPkg.Members { - members = append(members, name) - } - sort.Slice(members, func(i, j int) bool { - iPos := ssaPkg.Members[members[i]].Pos() - jPos := ssaPkg.Members[members[j]].Pos() - if i == j { - // Cannot sort by pos, so do it by name. - return members[i] < members[j] - } - return iPos < jPos - }) - - // Define all functions. + // Compile all functions, methods, and global variables in this package. irbuilder := c.ctx.NewBuilder() defer irbuilder.Dispose() - for _, name := range members { - member := ssaPkg.Members[name] - switch member := member.(type) { - case *ssa.Function: - if member.Blocks == nil { - continue // external function - } - // Create the function definition. - b := newBuilder(c, irbuilder, member) - b.createFunction() - } - } + c.createPackage(irbuilder, ssaPkg) return c.mod, nil } @@ -759,6 +721,83 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata { return c.difiles[filename] } +// createPackage builds the LLVM IR for all types, methods, and global variables +// in the given package. +func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) { + // Sort by position, so that the order of the functions in the IR matches + // the order of functions in the source file. This is useful for testing, + // for example. + var members []string + for name := range pkg.Members { + members = append(members, name) + } + sort.Slice(members, func(i, j int) bool { + iPos := pkg.Members[members[i]].Pos() + jPos := pkg.Members[members[j]].Pos() + if i == j { + // Cannot sort by pos, so do it by name. + return members[i] < members[j] + } + return iPos < jPos + }) + + // Define all functions. + for _, name := range members { + member := pkg.Members[name] + switch member := member.(type) { + case *ssa.Function: + if member.Blocks == nil { + continue // external function + } + // Create the function definition. + b := newBuilder(c, irbuilder, member) + b.createFunction() + case *ssa.Type: + if types.IsInterface(member.Type()) { + // Interfaces don't have concrete methods. + continue + } + + // Named type. We should make sure all methods are created. + // This includes both functions with pointer receivers and those + // without. + methods := getAllMethods(pkg.Prog, member.Type()) + methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...) + for _, method := range methods { + // Parse this method. + fn := pkg.Prog.MethodValue(method) + if fn.Blocks == nil { + continue // external function + } + if member.Type().String() != member.String() { + // This is a member on a type alias. Do not build such a + // function. + continue + } + if fn.Synthetic != "" && fn.Synthetic != "package initializer" { + // This function is a kind of wrapper function (created by + // the ssa package, not appearing in the source code) that + // is created by the getFunction method as needed. + // Therefore, don't build it here to avoid "function + // redeclared" errors. + continue + } + // Create the function definition. + b := newBuilder(c, irbuilder, fn) + b.createFunction() + } + case *ssa.Global: + // Global variable. + info := c.getGlobalInfo(member) + if !info.extern { + global := c.getGlobal(member) + global.SetInitializer(llvm.ConstNull(global.Type().ElementType())) + global.SetLinkage(llvm.InternalLinkage) + } + } + } +} + // createFunction builds the LLVM IR implementation for this function. The // function must not yet be defined, otherwise this function will create a // diagnostic. @@ -767,7 +806,7 @@ func (b *builder) createFunction() { fmt.Printf("\nfunc %s:\n", b.fn) } if !b.llvmFn.IsDeclaration() { - errValue := b.fn.Name() + " redeclared in this program" + errValue := b.llvmFn.Name() + " redeclared in this program" fnPos := getPosition(b.llvmFn) if fnPos.IsValid() { errValue += "\n\tprevious declaration at " + fnPos.String() @@ -948,6 +987,12 @@ func (b *builder) createFunction() { b.trackValue(phi.llvm) } } + + // Create anonymous functions (closures etc.). + for _, sub := range b.fn.AnonFuncs { + b := newBuilder(b.compilerContext, b.Builder, sub) + b.createFunction() + } } // createInstruction builds the LLVM IR equivalent instructions for the diff --git a/compiler/passes.go b/compiler/passes.go deleted file mode 100644 index 092f5f0b..00000000 --- a/compiler/passes.go +++ /dev/null @@ -1,164 +0,0 @@ -package compiler - -// This file implements a simple reachability analysis, to reduce compile time. -// This DCE pass used to be necessary for improving other passes but now it -// isn't necessary anymore. - -import ( - "errors" - "go/types" - "sort" - - "github.com/tinygo-org/tinygo/loader" - "golang.org/x/tools/go/ssa" -) - -type dceState struct { - *compilerContext - functions []*dceFunction - functionMap map[*ssa.Function]*dceFunction -} - -type dceFunction struct { - *ssa.Function - functionInfo - flag bool // used by dead code elimination -} - -func (p *dceState) addFunction(ssaFn *ssa.Function) { - if _, ok := p.functionMap[ssaFn]; ok { - return - } - f := &dceFunction{Function: ssaFn} - f.functionInfo = p.getFunctionInfo(ssaFn) - p.functions = append(p.functions, f) - p.functionMap[ssaFn] = f - - for _, anon := range ssaFn.AnonFuncs { - p.addFunction(anon) - } -} - -// simpleDCE returns a list of alive functions in the program. Compiling only -// these functions makes the compiler faster. -// -// This functionality will likely be replaced in the future with build caching. -func (c *compilerContext) simpleDCE(lprogram *loader.Program) ([]*ssa.Function, error) { - mainPkg := c.program.Package(lprogram.MainPkg().Pkg) - if mainPkg == nil { - panic("could not find main package") - } - p := &dceState{ - compilerContext: c, - functionMap: make(map[*ssa.Function]*dceFunction), - } - - for _, pkg := range lprogram.Sorted() { - pkg := c.program.Package(pkg.Pkg) - memberNames := make([]string, 0) - for name := range pkg.Members { - memberNames = append(memberNames, name) - } - sort.Strings(memberNames) - - for _, name := range memberNames { - member := pkg.Members[name] - switch member := member.(type) { - case *ssa.Function: - p.addFunction(member) - case *ssa.Type: - methods := getAllMethods(pkg.Prog, member.Type()) - if !types.IsInterface(member.Type()) { - // named type - for _, method := range methods { - p.addFunction(pkg.Prog.MethodValue(method)) - } - } - case *ssa.Global: - // Ignore. Globals are not handled here. - case *ssa.NamedConst: - // Ignore: these are already resolved. - default: - panic("unknown member type: " + member.String()) - } - } - } - - // Initial set of live functions. Include main.main, *.init and runtime.* - // functions. - main, ok := mainPkg.Members["main"].(*ssa.Function) - if !ok { - if mainPkg.Members["main"] == nil { - return nil, errors.New("function main is undeclared in the main package") - } else { - return nil, errors.New("cannot declare main - must be func") - } - } - runtimePkg := c.program.ImportedPackage("runtime") - mathPkg := c.program.ImportedPackage("math") - taskPkg := c.program.ImportedPackage("internal/task") - p.functionMap[main].flag = true - worklist := []*ssa.Function{main} - for _, f := range p.functions { - if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || f.Pkg == taskPkg || (f.Pkg == mathPkg && f.Pkg != nil) { - if f.flag { - continue - } - f.flag = true - worklist = append(worklist, f.Function) - } - } - - // Mark all called functions recursively. - for len(worklist) != 0 { - f := worklist[len(worklist)-1] - worklist = worklist[:len(worklist)-1] - for _, block := range f.Blocks { - for _, instr := range block.Instrs { - if instr, ok := instr.(*ssa.MakeInterface); ok { - for _, sel := range getAllMethods(c.program, instr.X.Type()) { - fn := c.program.MethodValue(sel) - callee := p.functionMap[fn] - if callee == nil { - // TODO: why is this necessary? - p.addFunction(fn) - callee = p.functionMap[fn] - } - if !callee.flag { - callee.flag = true - worklist = append(worklist, callee.Function) - } - } - } - for _, operand := range instr.Operands(nil) { - if operand == nil || *operand == nil { - continue - } - switch operand := (*operand).(type) { - case *ssa.Function: - f := p.functionMap[operand] - if f == nil { - // FIXME HACK: this function should have been - // discovered already. It is not for bound methods. - p.addFunction(operand) - f = p.functionMap[operand] - } - if !f.flag { - f.flag = true - worklist = append(worklist, operand) - } - } - } - } - } - } - - // Return all live functions. - liveFunctions := []*ssa.Function{} - for _, f := range p.functions { - if f.flag { - liveFunctions = append(liveFunctions, f.Function) - } - } - return liveFunctions, nil -} diff --git a/compiler/symbol.go b/compiler/symbol.go index b819b5ab..ea0b01d1 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -172,6 +172,21 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value { } } + // Synthetic functions are functions that do not appear in the source code, + // they are artificially constructed. Usually they are wrapper functions + // that are not referenced anywhere except in a SSA call instruction so + // should be created right away. + // The exception is the package initializer, which does appear in the + // *ssa.Package members and so shouldn't be created here. + if fn.Synthetic != "" && fn.Synthetic != "package initializer" { + irbuilder := c.ctx.NewBuilder() + b := newBuilder(c, irbuilder, fn) + b.createFunction() + irbuilder.Dispose() + llvmFn.SetLinkage(llvm.InternalLinkage) + llvmFn.SetUnnamedAddr(true) + } + return llvmFn } @@ -289,25 +304,22 @@ type globalInfo struct { // loadASTComments loads comments on globals from the AST, for use later in the // program. In particular, they are required for //go:extern pragmas on globals. -func (c *compilerContext) loadASTComments(lprogram *loader.Program) { - c.astComments = map[string]*ast.CommentGroup{} - for _, pkgInfo := range lprogram.Sorted() { - for _, file := range pkgInfo.Files { - for _, decl := range file.Decls { - switch decl := decl.(type) { - case *ast.GenDecl: - switch decl.Tok { - case token.VAR: - if len(decl.Specs) != 1 { - continue - } - for _, spec := range decl.Specs { - switch spec := spec.(type) { - case *ast.ValueSpec: // decl.Tok == token.VAR - for _, name := range spec.Names { - id := pkgInfo.Pkg.Path() + "." + name.Name - c.astComments[id] = decl.Doc - } +func (c *compilerContext) loadASTComments(pkg *loader.Package) { + for _, file := range pkg.Files { + for _, decl := range file.Decls { + switch decl := decl.(type) { + case *ast.GenDecl: + switch decl.Tok { + case token.VAR: + if len(decl.Specs) != 1 { + continue + } + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.ValueSpec: // decl.Tok == token.VAR + for _, name := range spec.Names { + id := pkg.Pkg.Path() + "." + name.Name + c.astComments[id] = decl.Doc } } } @@ -326,10 +338,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value { typ := g.Type().(*types.Pointer).Elem() llvmType := c.getLLVMType(typ) llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName) - if !info.extern { - llvmGlobal.SetInitializer(llvm.ConstNull(llvmType)) - llvmGlobal.SetLinkage(llvm.InternalLinkage) - } // Set alignment from the //go:align comment. var alignInBits uint32 @@ -347,8 +355,6 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value { if c.Debug && !info.extern { // Add debug info. - // TODO: this should be done for every global in the program, not just - // the ones that are referenced from some code. pos := c.program.Fset.Position(g.Pos()) diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{ Name: g.RelString(nil),