Update prebuilts to go1.11 ab/4979655

Test: m -j blueprint_tools
Change-Id: Ia7faf31e862a3eb235f48abc03bdbde2f3d06881
diff --git a/test/run.go b/test/run.go
index 22ec757..99ef79f 100644
--- a/test/run.go
+++ b/test/run.go
@@ -5,9 +5,6 @@
 // license that can be found in the LICENSE file.
 
 // Run runs tests in the test directory.
-//
-// TODO(bradfitz): docs of some sort, once we figure out how we're changing
-// headers of files
 package main
 
 import (
@@ -52,7 +49,7 @@
 
 	// dirs are the directories to look for *.go files in.
 	// TODO(bradfitz): just use all directories?
-	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs"}
+	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen"}
 
 	// ratec controls the max number of tests running at a time.
 	ratec chan bool
@@ -167,6 +164,22 @@
 	return p
 }
 
+// goTool reports the path of the go tool to use to run the tests.
+// If possible, use the same Go used to run run.go, otherwise
+// fallback to the go version found in the PATH.
+func goTool() string {
+	var exeSuffix string
+	if runtime.GOOS == "windows" {
+		exeSuffix = ".exe"
+	}
+	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
+	if _, err := os.Stat(path); err == nil {
+		return path
+	}
+	// Just run "go" from PATH
+	return "go"
+}
+
 func shardMatch(name string) bool {
 	if *shards == 0 {
 		return true
@@ -194,7 +207,7 @@
 type runCmd func(...string) ([]byte, error)
 
 func compileFile(runcmd runCmd, longname string, flags []string) (out []byte, err error) {
-	cmd := []string{"go", "tool", "compile", "-e"}
+	cmd := []string{goTool(), "tool", "compile", "-e"}
 	cmd = append(cmd, flags...)
 	if *linkshared {
 		cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
@@ -203,8 +216,12 @@
 	return runcmd(cmd...)
 }
 
-func compileInDir(runcmd runCmd, dir string, flags []string, names ...string) (out []byte, err error) {
-	cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."}
+func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) {
+	cmd := []string{goTool(), "tool", "compile", "-e"}
+	if localImports {
+		// Set relative path for local imports and import search path to current dir.
+		cmd = append(cmd, "-D", ".", "-I", ".")
+	}
 	cmd = append(cmd, flags...)
 	if *linkshared {
 		cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
@@ -217,7 +234,7 @@
 
 func linkFile(runcmd runCmd, goname string) (err error) {
 	pfile := strings.Replace(goname, ".go", ".o", -1)
-	cmd := []string{"go", "tool", "link", "-w", "-o", "a.exe", "-L", "."}
+	cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."}
 	if *linkshared {
 		cmd = append(cmd, "-linkshared", "-installsuffix=dynlink")
 	}
@@ -476,6 +493,7 @@
 	wantError := false
 	wantAuto := false
 	singlefilepkgs := false
+	localImports := true
 	f := strings.Fields(action)
 	if len(f) > 0 {
 		action = f[0]
@@ -484,11 +502,7 @@
 
 	// TODO: Clean up/simplify this switch statement.
 	switch action {
-	case "rundircmpout":
-		action = "rundir"
-	case "cmpout":
-		action = "run" // the run case already looks for <dir>/<test>.out files
-	case "compile", "compiledir", "build", "builddir", "run", "buildrun", "runoutput", "rundir":
+	case "compile", "compiledir", "build", "builddir", "buildrundir", "run", "buildrun", "runoutput", "rundir", "asmcheck":
 		// nothing to do
 	case "errorcheckandrundir":
 		wantError = false // should be no error if also will run
@@ -511,10 +525,18 @@
 	// collect flags
 	for len(args) > 0 && strings.HasPrefix(args[0], "-") {
 		switch args[0] {
+		case "-1":
+			wantError = true
 		case "-0":
 			wantError = false
 		case "-s":
 			singlefilepkgs = true
+		case "-n":
+			// Do not set relative path for local imports to current dir,
+			// e.g. do not pass -D . -I . to the compiler.
+			// Used in fixedbugs/bug345.go to allow compilation and import of local pkg.
+			// See golang.org/issue/25635
+			localImports = false
 		case "-t": // timeout in seconds
 			args = args[1:]
 			var err error
@@ -593,9 +615,38 @@
 	default:
 		t.err = fmt.Errorf("unimplemented action %q", action)
 
+	case "asmcheck":
+		// Compile Go file and match the generated assembly
+		// against a set of regexps in comments.
+		ops := t.wantedAsmOpcodes(long)
+		for _, env := range ops.Envs() {
+			cmdline := []string{"build", "-gcflags", "-S"}
+			cmdline = append(cmdline, flags...)
+			cmdline = append(cmdline, long)
+			cmd := exec.Command(goTool(), cmdline...)
+			cmd.Env = append(os.Environ(), env.Environ()...)
+
+			var buf bytes.Buffer
+			cmd.Stdout, cmd.Stderr = &buf, &buf
+			if err := cmd.Run(); err != nil {
+				fmt.Println(env, "\n", cmd.Stderr)
+				t.err = err
+				return
+			}
+
+			t.err = t.asmCheck(buf.String(), long, env, ops[env])
+			if t.err != nil {
+				return
+			}
+		}
+		return
+
 	case "errorcheck":
+		// Compile Go file.
+		// Fail if wantError is true and compilation was successful and vice versa.
+		// Match errors produced by gc against errors in comments.
 		// TODO(gri) remove need for -C (disable printing of columns in error messages)
-		cmdline := []string{"go", "tool", "compile", "-C", "-e", "-o", "a.o"}
+		cmdline := []string{goTool(), "tool", "compile", "-C", "-e", "-o", "a.o"}
 		// No need to add -dynlink even if linkshared if we're just checking for errors...
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, long)
@@ -618,10 +669,11 @@
 		return
 
 	case "compile":
+		// Compile Go file.
 		_, t.err = compileFile(runcmd, long, flags)
 
 	case "compiledir":
-		// Compile all files in the directory in lexicographic order.
+		// Compile all files in the directory as packages in lexicographic order.
 		longdir := filepath.Join(cwd, t.goDirName())
 		pkgs, err := goDirPackages(longdir, singlefilepkgs)
 		if err != nil {
@@ -629,24 +681,31 @@
 			return
 		}
 		for _, gofiles := range pkgs {
-			_, t.err = compileInDir(runcmd, longdir, flags, gofiles...)
+			_, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...)
 			if t.err != nil {
 				return
 			}
 		}
 
 	case "errorcheckdir", "errorcheckandrundir":
-		// errorcheck all files in lexicographic order
-		// useful for finding importing errors
+		// Compile and errorCheck all files in the directory as packages in lexicographic order.
+		// If errorcheckdir and wantError, compilation of the last package must fail.
+		// If errorcheckandrundir and wantError, compilation of the package prior the last must fail.
 		longdir := filepath.Join(cwd, t.goDirName())
 		pkgs, err := goDirPackages(longdir, singlefilepkgs)
 		if err != nil {
 			t.err = err
 			return
 		}
+		errPkg := len(pkgs) - 1
+		if wantError && action == "errorcheckandrundir" {
+			// The last pkg should compiled successfully and will be run in next case.
+			// Preceding pkg must return an error from compileInDir.
+			errPkg--
+		}
 		for i, gofiles := range pkgs {
-			out, err := compileInDir(runcmd, longdir, flags, gofiles...)
-			if i == len(pkgs)-1 {
+			out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...)
+			if i == errPkg {
 				if wantError && err == nil {
 					t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
 					return
@@ -673,8 +732,10 @@
 		fallthrough
 
 	case "rundir":
-		// Compile all files in the directory in lexicographic order.
-		// then link as if the last file is the main package and run it
+		// Compile all files in the directory as packages in lexicographic order.
+		// In case of errorcheckandrundir, ignore failed compilation of the package before the last.
+		// Link as if the last file is the main package, run it.
+		// Verify the expected output.
 		longdir := filepath.Join(cwd, t.goDirName())
 		pkgs, err := goDirPackages(longdir, singlefilepkgs)
 		if err != nil {
@@ -682,8 +743,10 @@
 			return
 		}
 		for i, gofiles := range pkgs {
-			_, err := compileInDir(runcmd, longdir, flags, gofiles...)
-			if err != nil {
+			_, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...)
+			// Allow this package compilation fail based on conditions below;
+			// its errors were checked in previous case.
+			if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) {
 				t.err = err
 				return
 			}
@@ -709,14 +772,15 @@
 		}
 
 	case "build":
-		_, err := runcmd("go", "build", goGcflags(), "-o", "a.exe", long)
+		// Build Go file.
+		_, err := runcmd(goTool(), "build", goGcflags(), "-o", "a.exe", long)
 		if err != nil {
 			t.err = err
 		}
 
-	case "builddir":
+	case "builddir", "buildrundir":
 		// Build an executable from all the .go and .s files in a subdirectory.
-		useTmp = true
+		// Run it and verify its output in the buildrundir case.
 		longdir := filepath.Join(cwd, t.goDirName())
 		files, dirErr := ioutil.ReadDir(longdir)
 		if dirErr != nil {
@@ -735,7 +799,7 @@
 
 		}
 		var objs []string
-		cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"}
+		cmd := []string{goTool(), "tool", "compile", "-e", "-D", ".", "-I", ".", "-o", "go.o"}
 		if len(asms) > 0 {
 			cmd = append(cmd, "-asmhdr", "go_asm.h")
 		}
@@ -749,7 +813,7 @@
 		}
 		objs = append(objs, "go.o")
 		if len(asms) > 0 {
-			cmd = []string{"go", "tool", "asm", "-e", "-I", ".", "-o", "asm.o"}
+			cmd = []string{goTool(), "tool", "asm", "-e", "-I", ".", "-o", "asm.o"}
 			for _, file := range asms {
 				cmd = append(cmd, filepath.Join(longdir, file.Name()))
 			}
@@ -760,24 +824,36 @@
 			}
 			objs = append(objs, "asm.o")
 		}
-		cmd = []string{"go", "tool", "pack", "c", "all.a"}
+		cmd = []string{goTool(), "tool", "pack", "c", "all.a"}
 		cmd = append(cmd, objs...)
 		_, err = runcmd(cmd...)
 		if err != nil {
 			t.err = err
 			break
 		}
-		cmd = []string{"go", "tool", "link", "all.a"}
+		cmd = []string{goTool(), "tool", "link", "-o", "a.exe", "all.a"}
 		_, err = runcmd(cmd...)
 		if err != nil {
 			t.err = err
 			break
 		}
+		if action == "buildrundir" {
+			cmd = append(findExecCmd(), filepath.Join(t.tempDir, "a.exe"))
+			out, err := runcmd(cmd...)
+			if err != nil {
+				t.err = err
+				break
+			}
+			if strings.Replace(string(out), "\r\n", "\n", -1) != t.expectedOutput() {
+				t.err = fmt.Errorf("incorrect output\n%s", out)
+			}
+		}
 
-	case "buildrun": // build binary, then run binary, instead of go run. Useful for timeout tests where failure mode is infinite loop.
+	case "buildrun":
+		// Build an executable from Go file, then run it, verify its output.
+		// Useful for timeout tests where failure mode is infinite loop.
 		// TODO: not supported on NaCl
-		useTmp = true
-		cmd := []string{"go", "build", goGcflags(), "-o", "a.exe"}
+		cmd := []string{goTool(), "build", goGcflags(), "-o", "a.exe"}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
 		}
@@ -801,6 +877,9 @@
 		}
 
 	case "run":
+		// Run Go file if no special go command flags are provided;
+		// otherwise build an executable and run it.
+		// Verify the output.
 		useTmp = false
 		var out []byte
 		var err error
@@ -813,12 +892,12 @@
 			// Because we run lots of trivial test programs,
 			// the time adds up.
 			pkg := filepath.Join(t.tempDir, "pkg.a")
-			if _, err := runcmd("go", "tool", "compile", "-o", pkg, t.goFileName()); err != nil {
+			if _, err := runcmd(goTool(), "tool", "compile", "-o", pkg, t.goFileName()); err != nil {
 				t.err = err
 				return
 			}
 			exe := filepath.Join(t.tempDir, "test.exe")
-			cmd := []string{"go", "tool", "link", "-s", "-w"}
+			cmd := []string{goTool(), "tool", "link", "-s", "-w"}
 			cmd = append(cmd, "-o", exe, pkg)
 			if _, err := runcmd(cmd...); err != nil {
 				t.err = err
@@ -826,7 +905,7 @@
 			}
 			out, err = runcmd(append([]string{exe}, args...)...)
 		} else {
-			cmd := []string{"go", "run", goGcflags()}
+			cmd := []string{goTool(), "run", goGcflags()}
 			if *linkshared {
 				cmd = append(cmd, "-linkshared")
 			}
@@ -843,12 +922,14 @@
 		}
 
 	case "runoutput":
+		// Run Go file and write its output into temporary Go file.
+		// Run generated Go file and verify its output.
 		rungatec <- true
 		defer func() {
 			<-rungatec
 		}()
 		useTmp = false
-		cmd := []string{"go", "run", goGcflags()}
+		cmd := []string{goTool(), "run", goGcflags()}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
 		}
@@ -863,7 +944,7 @@
 			t.err = fmt.Errorf("write tempfile:%s", err)
 			return
 		}
-		cmd = []string{"go", "run", goGcflags()}
+		cmd = []string{goTool(), "run", goGcflags()}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
 		}
@@ -878,8 +959,10 @@
 		}
 
 	case "errorcheckoutput":
+		// Run Go file and write its output into temporary Go file.
+		// Compile and errorCheck generated Go file.
 		useTmp = false
-		cmd := []string{"go", "run", goGcflags()}
+		cmd := []string{goTool(), "run", goGcflags()}
 		if *linkshared {
 			cmd = append(cmd, "-linkshared")
 		}
@@ -895,7 +978,7 @@
 			t.err = fmt.Errorf("write tempfile:%s", err)
 			return
 		}
-		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
+		cmdline := []string{goTool(), "tool", "compile", "-e", "-o", "a.o"}
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, tfile)
 		out, err = runcmd(cmdline...)
@@ -973,6 +1056,17 @@
 	return res
 }
 
+// errorCheck matches errors in outStr against comments in source files.
+// For each line of the source files which should generate an error,
+// there should be a comment of the form // ERROR "regexp".
+// If outStr has an error for a line which has no such comment,
+// this function will report an error.
+// Likewise if outStr does not have an error for a line which has a comment,
+// or if the error message does not match the <regexp>.
+// The <regexp> syntax is Perl but its best to stick to egrep.
+//
+// Sources files are supplied as fullshort slice.
+// It consists of pairs: full path to source file and it's base name.
 func (t *test) errorCheck(outStr string, wantAuto bool, fullshort ...string) (err error) {
 	defer func() {
 		if *verbose && err != nil {
@@ -1115,7 +1209,7 @@
 		return
 	}
 	// Polish.
-	exec.Command("go", "fmt", file).CombinedOutput()
+	exec.Command(goTool(), "fmt", file).CombinedOutput()
 }
 
 // matchPrefix reports whether s is of the form ^(.*/)?prefix(:|[),
@@ -1226,6 +1320,264 @@
 	return
 }
 
+const (
+	// Regexp to match a single opcode check: optionally begin with "-" (to indicate
+	// a negative check), followed by a string literal enclosed in "" or ``. For "",
+	// backslashes must be handled.
+	reMatchCheck = `-?(?:\x60[^\x60]*\x60|"(?:[^"\\]|\\.)*")`
+)
+
+var (
+	// Regexp to split a line in code and comment, trimming spaces
+	rxAsmComment = regexp.MustCompile(`^\s*(.*?)\s*(?:\/\/\s*(.+)\s*)?$`)
+
+	// Regexp to extract an architecture check: architecture name, followed by semi-colon,
+	// followed by a comma-separated list of opcode checks.
+	rxAsmPlatform = regexp.MustCompile(`(\w+)(/\w+)?(/\w*)?:(` + reMatchCheck + `(?:,` + reMatchCheck + `)*)`)
+
+	// Regexp to extract a single opcoded check
+	rxAsmCheck = regexp.MustCompile(reMatchCheck)
+
+	// List of all architecture variants. Key is the GOARCH architecture,
+	// value[0] is the variant-changing environment variable, and values[1:]
+	// are the supported variants.
+	archVariants = map[string][]string{
+		"386":     {"GO386", "387", "sse2"},
+		"amd64":   {},
+		"arm":     {"GOARM", "5", "6", "7"},
+		"arm64":   {},
+		"mips":    {"GOMIPS", "hardfloat", "softfloat"},
+		"mips64":  {"GOMIPS64", "hardfloat", "softfloat"},
+		"ppc64":   {},
+		"ppc64le": {},
+		"s390x":   {},
+	}
+)
+
+// wantedAsmOpcode is a single asmcheck check
+type wantedAsmOpcode struct {
+	fileline string         // original source file/line (eg: "/path/foo.go:45")
+	line     int            // original source line
+	opcode   *regexp.Regexp // opcode check to be performed on assembly output
+	negative bool           // true if the check is supposed to fail rather than pass
+	found    bool           // true if the opcode check matched at least one in the output
+}
+
+// A build environment triplet separated by slashes (eg: linux/386/sse2).
+// The third field can be empty if the arch does not support variants (eg: "plan9/amd64/")
+type buildEnv string
+
+// Environ returns the environment it represents in cmd.Environ() "key=val" format
+// For instance, "linux/386/sse2".Environ() returns {"GOOS=linux", "GOARCH=386", "GO386=sse2"}
+func (b buildEnv) Environ() []string {
+	fields := strings.Split(string(b), "/")
+	if len(fields) != 3 {
+		panic("invalid buildEnv string: " + string(b))
+	}
+	env := []string{"GOOS=" + fields[0], "GOARCH=" + fields[1]}
+	if fields[2] != "" {
+		env = append(env, archVariants[fields[1]][0]+"="+fields[2])
+	}
+	return env
+}
+
+// asmChecks represents all the asmcheck checks present in a test file
+// The outer map key is the build triplet in which the checks must be performed.
+// The inner map key represent the source file line ("filename.go:1234") at which the
+// checks must be performed.
+type asmChecks map[buildEnv]map[string][]wantedAsmOpcode
+
+// Envs returns all the buildEnv in which at least one check is present
+func (a asmChecks) Envs() []buildEnv {
+	var envs []buildEnv
+	for e := range a {
+		envs = append(envs, e)
+	}
+	sort.Slice(envs, func(i, j int) bool {
+		return string(envs[i]) < string(envs[j])
+	})
+	return envs
+}
+
+func (t *test) wantedAsmOpcodes(fn string) asmChecks {
+	ops := make(asmChecks)
+
+	comment := ""
+	src, _ := ioutil.ReadFile(fn)
+	for i, line := range strings.Split(string(src), "\n") {
+		matches := rxAsmComment.FindStringSubmatch(line)
+		code, cmt := matches[1], matches[2]
+
+		// Keep comments pending in the comment variable until
+		// we find a line that contains some code.
+		comment += " " + cmt
+		if code == "" {
+			continue
+		}
+
+		// Parse and extract any architecture check from comments,
+		// made by one architecture name and multiple checks.
+		lnum := fn + ":" + strconv.Itoa(i+1)
+		for _, ac := range rxAsmPlatform.FindAllStringSubmatch(comment, -1) {
+			archspec, allchecks := ac[1:4], ac[4]
+
+			var arch, subarch, os string
+			switch {
+			case archspec[2] != "": // 3 components: "linux/386/sse2"
+				os, arch, subarch = archspec[0], archspec[1][1:], archspec[2][1:]
+			case archspec[1] != "": // 2 components: "386/sse2"
+				os, arch, subarch = "linux", archspec[0], archspec[1][1:]
+			default: // 1 component: "386"
+				os, arch, subarch = "linux", archspec[0], ""
+			}
+
+			if _, ok := archVariants[arch]; !ok {
+				log.Fatalf("%s:%d: unsupported architecture: %v", t.goFileName(), i+1, arch)
+			}
+
+			// Create the build environments corresponding the above specifiers
+			envs := make([]buildEnv, 0, 4)
+			if subarch != "" {
+				envs = append(envs, buildEnv(os+"/"+arch+"/"+subarch))
+			} else {
+				subarchs := archVariants[arch]
+				if len(subarchs) == 0 {
+					envs = append(envs, buildEnv(os+"/"+arch+"/"))
+				} else {
+					for _, sa := range archVariants[arch][1:] {
+						envs = append(envs, buildEnv(os+"/"+arch+"/"+sa))
+					}
+				}
+			}
+
+			for _, m := range rxAsmCheck.FindAllString(allchecks, -1) {
+				negative := false
+				if m[0] == '-' {
+					negative = true
+					m = m[1:]
+				}
+
+				rxsrc, err := strconv.Unquote(m)
+				if err != nil {
+					log.Fatalf("%s:%d: error unquoting string: %v", t.goFileName(), i+1, err)
+				}
+
+				// Compile the checks as regular expressions. Notice that we
+				// consider checks as matching from the beginning of the actual
+				// assembler source (that is, what is left on each line of the
+				// compile -S output after we strip file/line info) to avoid
+				// trivial bugs such as "ADD" matching "FADD". This
+				// doesn't remove genericity: it's still possible to write
+				// something like "F?ADD", but we make common cases simpler
+				// to get right.
+				oprx, err := regexp.Compile("^" + rxsrc)
+				if err != nil {
+					log.Fatalf("%s:%d: %v", t.goFileName(), i+1, err)
+				}
+
+				for _, env := range envs {
+					if ops[env] == nil {
+						ops[env] = make(map[string][]wantedAsmOpcode)
+					}
+					ops[env][lnum] = append(ops[env][lnum], wantedAsmOpcode{
+						negative: negative,
+						fileline: lnum,
+						line:     i + 1,
+						opcode:   oprx,
+					})
+				}
+			}
+		}
+		comment = ""
+	}
+
+	return ops
+}
+
+func (t *test) asmCheck(outStr string, fn string, env buildEnv, fullops map[string][]wantedAsmOpcode) (err error) {
+	// The assembly output contains the concatenated dump of multiple functions.
+	// the first line of each function begins at column 0, while the rest is
+	// indented by a tabulation. These data structures help us index the
+	// output by function.
+	functionMarkers := make([]int, 1)
+	lineFuncMap := make(map[string]int)
+
+	lines := strings.Split(outStr, "\n")
+	rxLine := regexp.MustCompile(fmt.Sprintf(`\((%s:\d+)\)\s+(.*)`, regexp.QuoteMeta(fn)))
+
+	for nl, line := range lines {
+		// Check if this line begins a function
+		if len(line) > 0 && line[0] != '\t' {
+			functionMarkers = append(functionMarkers, nl)
+		}
+
+		// Search if this line contains a assembly opcode (which is prefixed by the
+		// original source file/line in parenthesis)
+		matches := rxLine.FindStringSubmatch(line)
+		if len(matches) == 0 {
+			continue
+		}
+		srcFileLine, asm := matches[1], matches[2]
+
+		// Associate the original file/line information to the current
+		// function in the output; it will be useful to dump it in case
+		// of error.
+		lineFuncMap[srcFileLine] = len(functionMarkers) - 1
+
+		// If there are opcode checks associated to this source file/line,
+		// run the checks.
+		if ops, found := fullops[srcFileLine]; found {
+			for i := range ops {
+				if !ops[i].found && ops[i].opcode.FindString(asm) != "" {
+					ops[i].found = true
+				}
+			}
+		}
+	}
+	functionMarkers = append(functionMarkers, len(lines))
+
+	var failed []wantedAsmOpcode
+	for _, ops := range fullops {
+		for _, o := range ops {
+			// There's a failure if a negative match was found,
+			// or a positive match was not found.
+			if o.negative == o.found {
+				failed = append(failed, o)
+			}
+		}
+	}
+	if len(failed) == 0 {
+		return
+	}
+
+	// At least one asmcheck failed; report them
+	sort.Slice(failed, func(i, j int) bool {
+		return failed[i].line < failed[j].line
+	})
+
+	lastFunction := -1
+	var errbuf bytes.Buffer
+	fmt.Fprintln(&errbuf)
+	for _, o := range failed {
+		// Dump the function in which this opcode check was supposed to
+		// pass but failed.
+		funcIdx := lineFuncMap[o.fileline]
+		if funcIdx != 0 && funcIdx != lastFunction {
+			funcLines := lines[functionMarkers[funcIdx]:functionMarkers[funcIdx+1]]
+			log.Println(strings.Join(funcLines, "\n"))
+			lastFunction = funcIdx // avoid printing same function twice
+		}
+
+		if o.negative {
+			fmt.Fprintf(&errbuf, "%s:%d: %s: wrong opcode found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
+		} else {
+			fmt.Fprintf(&errbuf, "%s:%d: %s: opcode not found: %q\n", t.goFileName(), o.line, env, o.opcode.String())
+		}
+	}
+	err = errors.New(errbuf.String())
+	return
+}
+
 // defaultRunOutputLimit returns the number of runoutput tests that
 // can be executed in parallel.
 func defaultRunOutputLimit() int {