Regres: Optimize coverage processing

This had regressed mostly due to a single thread processing a huge number of `Span.Add()`s.

Optimize for the common case, avoid `Span.Add()` in places where the data is already sorted, don't scan the entire file system for every test run.

Bug: b/152192800
Change-Id: Id183468263e65bfbcf387ec1c978d8b9de547cee
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43570
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/tests/regres/cov/import.go b/tests/regres/cov/import.go
index 1328b50..5240dd2 100644
--- a/tests/regres/cov/import.go
+++ b/tests/regres/cov/import.go
@@ -27,15 +27,6 @@
 	"../llvm"
 )
 
-var ignorePaths = map[string]bool{
-	"src/Common":   true,
-	"src/Main":     true,
-	"src/OpenGL":   true,
-	"src/Renderer": true,
-	"src/Shader":   true,
-	"src/System":   true,
-}
-
 // File describes the coverage spans in a single source file.
 type File struct {
 	Path      string
@@ -63,6 +54,45 @@
 	return append(env, "LLVM_PROFILE_FILE="+coverageFile)
 }
 
+// AllSourceFiles returns a *Coverage containing all the source files without
+// coverage data. This populates the coverage view with files even if they
+// didn't get compiled.
+func (e Env) AllSourceFiles() *Coverage {
+	var ignorePaths = map[string]bool{
+		"src/Common":   true,
+		"src/Main":     true,
+		"src/OpenGL":   true,
+		"src/Renderer": true,
+		"src/Shader":   true,
+		"src/System":   true,
+	}
+
+	// Gather all the source files to include them even if there is no coverage
+	// information produced for these files. This highlights files that aren't
+	// even compiled.
+	cov := Coverage{}
+	allFiles := map[string]struct{}{}
+	filepath.Walk(filepath.Join(e.RootDir, "src"), func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		rel, err := filepath.Rel(e.RootDir, path)
+		if err != nil || ignorePaths[rel] {
+			return filepath.SkipDir
+		}
+		if !info.IsDir() {
+			switch filepath.Ext(path) {
+			case ".h", ".c", ".cc", ".cpp", ".hpp":
+				if _, seen := allFiles[rel]; !seen {
+					cov.Files = append(cov.Files, File{Path: rel})
+				}
+			}
+		}
+		return nil
+	})
+	return &cov
+}
+
 // Import uses the llvm-profdata and llvm-cov tools to import the coverage
 // information from a .profraw file.
 func (e Env) Import(profrawPath string) (*Coverage, error) {
@@ -108,32 +138,6 @@
 		return nil, cause.Wrap(err, "Couldn't process turbo-cov output")
 	}
 
-	// Gather all the source files to include them even if there is no coverage
-	// information produced for these files. This highlights files that aren't
-	// even compiled.
-	allFiles := map[string]struct{}{}
-	for _, file := range cov.Files {
-		allFiles[file.Path] = struct{}{}
-	}
-	filepath.Walk(filepath.Join(e.RootDir, "src"), func(path string, info os.FileInfo, err error) error {
-		if err != nil {
-			return err
-		}
-		rel, err := filepath.Rel(e.RootDir, path)
-		if err != nil || ignorePaths[rel] {
-			return filepath.SkipDir
-		}
-		if !info.IsDir() {
-			switch filepath.Ext(path) {
-			case ".h", ".c", ".cc", ".cpp", ".hpp":
-				if _, seen := allFiles[rel]; !seen {
-					cov.Files = append(cov.Files, File{Path: rel})
-				}
-			}
-		}
-		return nil
-	})
-
 	return cov, nil
 }
 
@@ -185,9 +189,9 @@
 			start := Location{(int)(f.Segments[sIdx][0].(float64)), (int)(f.Segments[sIdx][1].(float64))}
 			end := Location{(int)(f.Segments[sIdx+1][0].(float64)), (int)(f.Segments[sIdx+1][1].(float64))}
 			if covered := f.Segments[sIdx][2].(float64) != 0; covered {
-				file.Covered.Add(Span{start, end})
+				file.Covered = appendSpan(file.Covered, Span{start, end})
 			} else {
-				file.Uncovered.Add(Span{start, end})
+				file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
 			}
 		}
 		if len(file.Covered) > 0 {
@@ -251,9 +255,9 @@
 			end := segments[sIdx+1].location
 			if segments[sIdx].covered {
 				if segments[sIdx].count > 0 {
-					file.Covered.Add(Span{start, end})
+					file.Covered = appendSpan(file.Covered, Span{start, end})
 				} else {
-					file.Uncovered.Add(Span{start, end})
+					file.Uncovered = appendSpan(file.Uncovered, Span{start, end})
 				}
 			}
 		}
diff --git a/tests/regres/cov/span.go b/tests/regres/cov/span.go
index 9545936..36a11c4 100644
--- a/tests/regres/cov/span.go
+++ b/tests/regres/cov/span.go
@@ -76,6 +76,9 @@
 // Before returns true if span s comes before o.
 func (s Span) Before(o Span) bool { return s.Compare(o) == -1 }
 
+// Inside returns true if span s fits entirely inside o.
+func (s Span) Inside(o Span) bool { return s.Start.Compare(o.Start) >= 0 && s.End.Compare(o.End) <= 0 }
+
 // SpanList is a sorted list of spans. Use SpanList.Add() to insert new spans.
 type SpanList []Span
 
@@ -87,6 +90,11 @@
 	// [ 0 ] [ 1 ] [ 2 ] [ 3 ] | idxStart: 1 |  idxEnd: 2
 	// [0]  [1]  [2]  [3]  [4] | idxStart: 2 |  idxEnd: 2
 	idxStart := sort.Search(len(*l), func(i int) bool { return (*l)[i].End.Compare(s.Start) >= 0 })
+
+	if idxStart < len(*l) && s.Inside((*l)[idxStart]) {
+		return // No change.
+	}
+
 	idxEnd := sort.Search(len(*l), func(i int) bool { return (*l)[i].Start.Compare(s.End) > 0 })
 
 	if idxStart < idxEnd {
diff --git a/tests/regres/deqp/deqp.go b/tests/regres/deqp/deqp.go
index e44be99..33573bf 100644
--- a/tests/regres/deqp/deqp.go
+++ b/tests/regres/deqp/deqp.go
@@ -216,6 +216,7 @@
 
 	if c.CoverageEnv != nil {
 		out.Coverage = &cov.Tree{}
+		out.Coverage.Add(cov.Path{}, c.CoverageEnv.AllSourceFiles())
 	}
 
 	// Collect the results.