modernc.org/gc@v1.0.1-0.20240304020402-f0dba7c97c2b/testdata/errchk/test/fixedbugs/issue18902.go (about)

     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  }