diff --git a/builder/build.go b/builder/build.go index 104e0f386..974ddc2a3 100644 --- a/builder/build.go +++ b/builder/build.go @@ -24,6 +24,7 @@ import ( "sort" "strconv" "strings" + "sync" "github.com/gofrs/flock" "github.com/tinygo-org/tinygo/compileopts" @@ -269,6 +270,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe // Create the *ssa.Program. This does not yet build the entire SSA of the // program so it's pretty fast and doesn't need to be parallelized. program := lprogram.LoadSSA() + buildProgram := sync.OnceFunc(program.Build) // Add jobs to compile each package. // Packages that have a cache hit will not be compiled again. @@ -398,8 +400,13 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe return nil } - // Compile AST to IR. The compiler.CompilePackage function will - // build the SSA as needed. + // SSA package builds may run concurrently, but the resulting + // functions cannot be inspected until all builds have finished: + // generic instances and wrappers can be shared across packages. + // Build the whole program once before compiling any package. + buildProgram() + + // Compile AST to IR. mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA()) defer mod.Context().Dispose() defer mod.Dispose() diff --git a/compiler/compiler.go b/compiler/compiler.go index 19410d49e..51197bac3 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -296,6 +296,9 @@ func Sizes(machine llvm.TargetMachine) types.Sizes { } // CompilePackage compiles a single package to a LLVM module. +// +// The SSA package must already be built. When packages are compiled +// concurrently, the entire SSA program must be built before compilation starts. func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool) (llvm.Module, []error) { c := newCompilerContext(moduleName, machine, config, dumpSSA) defer c.dispose() @@ -306,9 +309,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, c.runtimePkg = ssaPkg.Prog.ImportedPackage("runtime").Pkg c.program = ssaPkg.Prog - // Convert AST to SSA. - ssaPkg.Build() - // Assign names to function-local named types before compiling the // package, so that types declared in different functions (or in // different instantiations of a generic function) do not collide. diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 99de5bc62..d9188790c 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -413,5 +413,7 @@ func testCompilePackage(t *testing.T, options *compileopts.Options, file string) // Compile AST to IR. program := lprogram.LoadSSA() pkg := lprogram.MainPkg() - return CompilePackage(file, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false) + ssaPkg := program.Package(pkg.Pkg) + ssaPkg.Build() + return CompilePackage(file, pkg, ssaPkg, machine, compilerConfig, false) } diff --git a/transform/transform_test.go b/transform/transform_test.go index 4af128387..89fda55e2 100644 --- a/transform/transform_test.go +++ b/transform/transform_test.go @@ -224,7 +224,9 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module { // Compile AST to IR. program := lprogram.LoadSSA() pkg := lprogram.MainPkg() - mod, errs := compiler.CompilePackage(filename, pkg, program.Package(pkg.Pkg), machine, compilerConfig, false) + ssaPkg := program.Package(pkg.Pkg) + ssaPkg.Build() + mod, errs := compiler.CompilePackage(filename, pkg, ssaPkg, machine, compilerConfig, false) if errs != nil { for _, err := range errs { t.Error(err)