Skip to content

Commit a590d79

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent cb147b9 commit a590d79

File tree

3 files changed

+30
-30
lines changed

3 files changed

+30
-30
lines changed

builder/build.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
124124
return err
125125
}
126126

127-
// The slice of jobs that orchestrates most of the build.
128-
// This is somewhat like an in-memory Makefile with each job being a
129-
// Makefile target.
130-
var jobs []*compileJob
131-
132127
// Create the *ssa.Program. This does not yet build the entire SSA of the
133128
// program so it's pretty fast and doesn't need to be parallelized.
134129
program := lprogram.LoadSSA()
@@ -311,7 +306,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
311306
return os.Rename(f.Name(), bitcodePath)
312307
},
313308
}
314-
jobs = append(jobs, job)
315309
packageJobs = append(packageJobs, job)
316310
}
317311

@@ -402,14 +396,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
402396
return nil
403397
},
404398
}
405-
jobs = append(jobs, programJob)
406399

407400
// Check whether we only need to create an object file.
408401
// If so, we don't need to link anything and will be finished quickly.
409402
outext := filepath.Ext(outpath)
410403
if outext == ".o" || outext == ".bc" || outext == ".ll" {
411404
// Run jobs to produce the LLVM module.
412-
err := runJobs(jobs)
405+
err := runJobs(programJob)
413406
if err != nil {
414407
return err
415408
}
@@ -450,7 +443,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
450443
return ioutil.WriteFile(objfile, llvmBuf.Bytes(), 0666)
451444
},
452445
}
453-
jobs = append(jobs, outputObjectFileJob)
454446

455447
// Prepare link command.
456448
linkerDependencies := []*compileJob{outputObjectFileJob}
@@ -465,8 +457,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
465457
if err != nil {
466458
return err
467459
}
468-
jobs = append(jobs, job.dependencies...)
469-
jobs = append(jobs, job)
470460
linkerDependencies = append(linkerDependencies, job)
471461
}
472462

@@ -484,7 +474,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
484474
return err
485475
},
486476
}
487-
jobs = append(jobs, job)
488477
linkerDependencies = append(linkerDependencies, job)
489478
}
490479

@@ -503,7 +492,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
503492
return err
504493
},
505494
}
506-
jobs = append(jobs, job)
507495
linkerDependencies = append(linkerDependencies, job)
508496
}
509497
}
@@ -521,17 +509,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
521509
if err != nil {
522510
return err
523511
}
524-
// The library needs to be compiled (cache miss).
525-
jobs = append(jobs, job.dependencies...)
526-
jobs = append(jobs, job)
527512
linkerDependencies = append(linkerDependencies, job)
528513
case "wasi-libc":
529514
path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
530515
if _, err := os.Stat(path); os.IsNotExist(err) {
531516
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
532517
}
533518
job := dummyCompileJob(path)
534-
jobs = append(jobs, job)
535519
linkerDependencies = append(linkerDependencies, job)
536520
case "":
537521
// no library specified, so nothing to do
@@ -574,7 +558,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
574558

575559
// Create a linker job, which links all object files together and does some
576560
// extra stuff that can only be done after linking.
577-
jobs = append(jobs, &compileJob{
561+
linkJob := &compileJob{
578562
description: "link",
579563
dependencies: linkerDependencies,
580564
run: func(job *compileJob) error {
@@ -647,12 +631,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
647631

648632
return nil
649633
},
650-
})
634+
}
651635

652636
// Run all jobs to compile and link the program.
653637
// Do this now (instead of after elf-to-hex and similar conversions) as it
654638
// is simpler and cannot be parallelized.
655-
err = runJobs(jobs)
639+
err = runJobs(linkJob)
656640
if err != nil {
657641
return err
658642
}

builder/jobs.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,29 @@ func (job *compileJob) readyToRun() bool {
6565
return true
6666
}
6767

68-
// runJobs runs all the jobs indicated in the jobs slice and returns the error
69-
// of the first job that fails to run.
70-
// It runs all jobs in the order of the slice, as long as all dependencies have
71-
// already run. Therefore, if some jobs are preferred to run before others, they
72-
// should be ordered as such in this slice.
73-
func runJobs(jobs []*compileJob) error {
68+
// runJobs runs the indicated job and all its dependencies. For every job, all
69+
// the dependencies are run first. It returns the error of the first job that
70+
// fails.
71+
// It runs all jobs in the order of the dependencies slice, depth-first.
72+
// Therefore, if some jobs are preferred to run before others, they should be
73+
// ordered as such in the job dependencies.
74+
func runJobs(job *compileJob) error {
75+
// Create a slice of jobs to run, where all dependencies are run in order.
76+
jobs := []*compileJob{}
77+
addedJobs := map[*compileJob]struct{}{}
78+
var addJobs func(*compileJob)
79+
addJobs = func(job *compileJob) {
80+
if _, ok := addedJobs[job]; ok {
81+
return
82+
}
83+
for _, dep := range job.dependencies {
84+
addJobs(dep)
85+
}
86+
jobs = append(jobs, job)
87+
addedJobs[job] = struct{}{}
88+
}
89+
addJobs(job)
90+
7491
// Create channels to communicate with the workers.
7592
doneChan := make(chan *compileJob)
7693
workerChan := make(chan *compileJob)

builder/library.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,14 @@ func (l *Library) Load(target, tmpdir string) (path string, err error) {
4646
if err != nil {
4747
return "", err
4848
}
49-
jobs := append([]*compileJob{job}, job.dependencies...)
50-
err = runJobs(jobs)
49+
err = runJobs(job)
5150
return job.result, err
5251
}
5352

5453
// load returns a compile job to build this library file for the given target
5554
// and CPU. It may return a dummy compileJob if the library build is already
56-
// cached. The path is stored as job.result but is only valid if the job and
57-
// job.dependencies have been run.
55+
// cached. The path is stored as job.result but is only valid after the job has
56+
// been run.
5857
// The provided tmpdir will be used to store intermediary files and possibly the
5958
// output archive file, it is expected to be removed after use.
6059
func (l *Library) load(target, cpu, tmpdir string) (job *compileJob, err error) {

0 commit comments

Comments
 (0)