builder: simplify running of jobs

Instead of keeping a slice of jobs to run, let the runJobs function
determine which jobs should be run by investigating all dependencies.
This has two benefits:

  - The code is somewhat cleaner, as no 'jobs' slice needs to be
    maintained while constructing the dependency graph.
  - Eventually, some jobs might not be required by any dependency.
    While it's possible to avoid adding them to the slice, the simpler
    solution is to build a new slice from the dependencies which will
    only include required dependencies by design.
Этот коммит содержится в:
Ayke van Laethem 2021-04-09 14:08:26 +02:00 коммит произвёл Ron Evans
родитель cb147b9475
коммит a590d791bd
3 изменённых файлов: 30 добавлений и 30 удалений

Просмотреть файл

@ -124,11 +124,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err return err
} }
// The slice of jobs that orchestrates most of the build.
// This is somewhat like an in-memory Makefile with each job being a
// Makefile target.
var jobs []*compileJob
// Create the *ssa.Program. This does not yet build the entire SSA of the // 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 so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA() program := lprogram.LoadSSA()
@ -311,7 +306,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return os.Rename(f.Name(), bitcodePath) return os.Rename(f.Name(), bitcodePath)
}, },
} }
jobs = append(jobs, job)
packageJobs = append(packageJobs, job) packageJobs = append(packageJobs, job)
} }
@ -402,14 +396,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return nil return nil
}, },
} }
jobs = append(jobs, programJob)
// Check whether we only need to create an object file. // Check whether we only need to create an object file.
// If so, we don't need to link anything and will be finished quickly. // If so, we don't need to link anything and will be finished quickly.
outext := filepath.Ext(outpath) outext := filepath.Ext(outpath)
if outext == ".o" || outext == ".bc" || outext == ".ll" { if outext == ".o" || outext == ".bc" || outext == ".ll" {
// Run jobs to produce the LLVM module. // Run jobs to produce the LLVM module.
err := runJobs(jobs) err := runJobs(programJob)
if err != nil { if err != nil {
return err return err
} }
@ -450,7 +443,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666) return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
}, },
} }
jobs = append(jobs, outputObjectFileJob)
// Prepare link command. // Prepare link command.
linkerDependencies := []*compileJob{outputObjectFileJob} linkerDependencies := []*compileJob{outputObjectFileJob}
@ -465,8 +457,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
} }
@ -484,7 +474,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err return err
}, },
} }
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
} }
@ -503,7 +492,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return err return err
}, },
} }
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
} }
} }
@ -521,9 +509,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if err != nil { if err != nil {
return err return err
} }
// The library needs to be compiled (cache miss).
jobs = append(jobs, job.dependencies...)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
case "wasi-libc": case "wasi-libc":
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a") path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
@ -531,7 +516,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?") return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
} }
job := dummyCompileJob(path) job := dummyCompileJob(path)
jobs = append(jobs, job)
linkerDependencies = append(linkerDependencies, job) linkerDependencies = append(linkerDependencies, job)
case "": case "":
// no library specified, so nothing to do // no library specified, so nothing to do
@ -574,7 +558,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
// Create a linker job, which links all object files together and does some // Create a linker job, which links all object files together and does some
// extra stuff that can only be done after linking. // extra stuff that can only be done after linking.
jobs = append(jobs, &compileJob{ linkJob := &compileJob{
description: "link", description: "link",
dependencies: linkerDependencies, dependencies: linkerDependencies,
run: func(job *compileJob) error { run: func(job *compileJob) error {
@ -647,12 +631,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
return nil return nil
}, },
}) }
// Run all jobs to compile and link the program. // Run all jobs to compile and link the program.
// Do this now (instead of after elf-to-hex and similar conversions) as it // Do this now (instead of after elf-to-hex and similar conversions) as it
// is simpler and cannot be parallelized. // is simpler and cannot be parallelized.
err = runJobs(jobs) err = runJobs(linkJob)
if err != nil { if err != nil {
return err return err
} }

Просмотреть файл

@ -65,12 +65,29 @@ func (job *compileJob) readyToRun() bool {
return true return true
} }
// runJobs runs all the jobs indicated in the jobs slice and returns the error // runJobs runs the indicated job and all its dependencies. For every job, all
// of the first job that fails to run. // the dependencies are run first. It returns the error of the first job that
// It runs all jobs in the order of the slice, as long as all dependencies have // fails.
// already run. Therefore, if some jobs are preferred to run before others, they // It runs all jobs in the order of the dependencies slice, depth-first.
// should be ordered as such in this slice. // Therefore, if some jobs are preferred to run before others, they should be
func runJobs(jobs []*compileJob) error { // ordered as such in the job dependencies.
func runJobs(job *compileJob) error {
// Create a slice of jobs to run, where all dependencies are run in order.
jobs := []*compileJob{}
addedJobs := map[*compileJob]struct{}{}
var addJobs func(*compileJob)
addJobs = func(job *compileJob) {
if _, ok := addedJobs[job]; ok {
return
}
for _, dep := range job.dependencies {
addJobs(dep)
}
jobs = append(jobs, job)
addedJobs[job] = struct{}{}
}
addJobs(job)
// Create channels to communicate with the workers. // Create channels to communicate with the workers.
doneChan := make(chan *compileJob) doneChan := make(chan *compileJob)
workerChan := make(chan *compileJob) workerChan := make(chan *compileJob)

Просмотреть файл

@ -46,15 +46,14 @@ func (l *Library) Load(target, tmpdir string) (path string, err error) {
if err != nil { if err != nil {
return "", err return "", err
} }
jobs := append([]*compileJob{job}, job.dependencies...) err = runJobs(job)
err = runJobs(jobs)
return job.result, err return job.result, err
} }
// load returns a compile job to build this library file for the given target // load returns a compile job to build this library file for the given target
// and CPU. It may return a dummy compileJob if the library build is already // and CPU. It may return a dummy compileJob if the library build is already
// cached. The path is stored as job.result but is only valid if the job and // cached. The path is stored as job.result but is only valid after the job has
// job.dependencies have been run. // been run.
// The provided tmpdir will be used to store intermediary files and possibly the // The provided tmpdir will be used to store intermediary files and possibly the
// output archive file, it is expected to be removed after use. // output archive file, it is expected to be removed after use.
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) { func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {