Home | History | Annotate | Download | only in fixedbugs
      1 // run
      2 // +build !nacl
      3 
      4 // Copyright 2016 The Go Authors. All rights reserved.
      5 // Use of this source code is governed by a BSD-style
      6 // license that can be found in the LICENSE file.
      7 
      8 // Runs a build -S to capture the assembly language
      9 // output, checks that the line numbers associated with
     10 // the stream of instructions do not change "too much".
     11 // The changes that fixes this (that reduces the amount
     12 // of change) does so by treating register spill, reload,
     13 // copy, and rematerializations as being "unimportant" and
     14 // just assigns them the line numbers of whatever "real"
     15 // instructions preceded them.
     16 
     17 // nacl is excluded because this runs a compiler.
     18 
     19 package main
     20 
     21 import (
     22 	"bufio"
     23 	"bytes"
     24 	"fmt"
     25 	"os"
     26 	"os/exec"
     27 	"strconv"
     28 	"strings"
     29 )
     30 
     31 // updateEnv modifies env to ensure that key=val
     32 func updateEnv(env *[]string, key, val string) {
     33 	if val != "" {
     34 		var found bool
     35 		key = key + "="
     36 		for i, kv := range *env {
     37 			if strings.HasPrefix(kv, key) {
     38 				(*env)[i] = key + val
     39 				found = true
     40 				break
     41 			}
     42 		}
     43 		if !found {
     44 			*env = append(*env, key+val)
     45 		}
     46 	}
     47 }
     48 
     49 func main() {
     50 	testarch := os.Getenv("TESTARCH")     // Targets other platform in test compilation.
     51 	debug := os.Getenv("TESTDEBUG") != "" // Output the relevant assembly language.
     52 
     53 	cmd := exec.Command("go", "tool", "compile", "-S", "fixedbugs/issue18902b.go")
     54 	var buf bytes.Buffer
     55 	cmd.Stdout = &buf
     56 	cmd.Stderr = &buf
     57 	cmd.Env = os.Environ()
     58 
     59 	if testarch != "" {
     60 		updateEnv(&cmd.Env, "GOARCH", testarch)
     61 		updateEnv(&cmd.Env, "GOOS", "linux") // Simplify multi-arch testing
     62 	}
     63 
     64 	err := cmd.Run()
     65 	if err != nil {
     66 		fmt.Printf("%s\n%s", err, buf.Bytes())
     67 		return
     68 	}
     69 	begin := "\"\".(*gcSortBuf).flush" // Text at beginning of relevant dissassembly.
     70 	s := buf.String()
     71 	i := strings.Index(s, begin)
     72 	if i < 0 {
     73 		fmt.Printf("Failed to find expected symbol %s in output\n%s\n", begin, s)
     74 		return
     75 	}
     76 	s = s[i:]
     77 	r := strings.NewReader(s)
     78 	scanner := bufio.NewScanner(r)
     79 	first := true                         // The first line after the begin text will be skipped
     80 	beforeLineNumber := "issue18902b.go:" // Text preceding line number in each line.
     81 	lbln := len(beforeLineNumber)
     82 
     83 	var scannedCount, changes, sumdiffs float64
     84 
     85 	prevVal := 0
     86 	for scanner.Scan() {
     87 		line := scanner.Text()
     88 		if first {
     89 			first = false
     90 			continue
     91 		}
     92 		i = strings.Index(line, beforeLineNumber)
     93 		if i < 0 {
     94 			// Done reading lines
     95 			const minLines = 150
     96 			if scannedCount <= minLines { // When test was written, 251 lines observed on amd64; arm64 now obtains 184
     97 				fmt.Printf("Scanned only %d lines, was expecting more than %d\n", int(scannedCount), minLines)
     98 				return
     99 			}
    100 			// Note: when test was written, before changes=92, after=50 (was 62 w/o rematerialization NoXPos in *Value.copyInto())
    101 			// and before sumdiffs=784, after=180 (was 446 w/o rematerialization NoXPos in *Value.copyInto())
    102 			// Set the dividing line between pass and fail at the midpoint.
    103 			// Normalize against instruction count in case we unroll loops, etc.
    104 			if changes/scannedCount >= (50+92)/(2*scannedCount) || sumdiffs/scannedCount >= (180+784)/(2*scannedCount) {
    105 				fmt.Printf("Line numbers change too much, # of changes=%.f, sumdiffs=%.f, # of instructions=%.f\n", changes, sumdiffs, scannedCount)
    106 			}
    107 			return
    108 		}
    109 		scannedCount++
    110 		i += lbln
    111 		lineVal, err := strconv.Atoi(line[i : i+3])
    112 		if err != nil {
    113 			fmt.Printf("Expected 3-digit line number after %s in %s\n", beforeLineNumber, line)
    114 		}
    115 		if prevVal == 0 {
    116 			prevVal = lineVal
    117 		}
    118 		diff := lineVal - prevVal
    119 		if diff < 0 {
    120 			diff = -diff
    121 		}
    122 		if diff != 0 {
    123 			changes++
    124 			sumdiffs += float64(diff)
    125 		}
    126 		// If things change too much, set environment variable TESTDEBUG to help figure out what's up.
    127 		// The "before" behavior can be recreated in DebugFriendlySetPosFrom (currently in gc/ssa.go)
    128 		// by inserting unconditional
    129 		//   	s.SetPos(v.Pos)
    130 		// at the top of the function.
    131 
    132 		if debug {
    133 			fmt.Printf("%d %.f %.f %s\n", lineVal, changes, sumdiffs, line)
    134 		}
    135 		prevVal = lineVal
    136 	}
    137 	if err := scanner.Err(); err != nil {
    138 		fmt.Println("Reading standard input:", err)
    139 		return
    140 	}
    141 }
    142