]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add ability to hash-debug on file:line, including inlining
authorDavid Chase <drchase@google.com>
Mon, 7 Nov 2022 16:02:08 +0000 (11:02 -0500)
committerDavid Chase <drchase@google.com>
Thu, 10 Nov 2022 17:02:09 +0000 (17:02 +0000)
Modified the fmahash gc debug flag to use this, and modified the
test to check for a hash match that includes inlining.  Also
made the test non-short to ensure portability.

Note fma.go has been enhanced into an FMA test that requires
two separate FMAs in order to "fail"; if either one is 2-rounding,
then it "passes".  (It neither passes nor fails here; its role
is to demonstrate that the FMAs are correctly reported; the
enhanced failure mode was discovered while testing the search
tool.)

Change-Id: I4e328e3654f442d498eac982135420abb59c5434
Reviewed-on: https://go-review.googlesource.com/c/go/+/448358
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: David Chase <drchase@google.com>

src/cmd/compile/internal/base/hashdebug.go
src/cmd/compile/internal/ssa/fmahash_test.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssa/testdata/fma.go

index 609f80393e954428c9940295c90e262cd66b0689..6c4821bbf662fbf18846e88a658216717f98781a 100644 (file)
@@ -5,7 +5,10 @@
 package base
 
 import (
+       "bytes"
        "cmd/internal/notsha256"
+       "cmd/internal/obj"
+       "cmd/internal/src"
        "fmt"
        "io"
        "os"
@@ -27,13 +30,15 @@ type hashAndMask struct {
 }
 
 type HashDebug struct {
-       mu   sync.Mutex
-       name string // base name of the flag/variable.
+       mu   sync.Mutex // for logfile, posTmp, bytesTmp
+       name string     // base name of the flag/variable.
        // what file (if any) receives the yes/no logging?
        // default is os.Stdout
-       logfile writeSyncer
-       matches []hashAndMask // A hash matches if one of these matches.
-       yes, no bool
+       logfile  writeSyncer
+       posTmp   []src.Pos
+       bytesTmp bytes.Buffer
+       matches  []hashAndMask // A hash matches if one of these matches.
+       yes, no  bool
 }
 
 // The default compiler-debugging HashDebug, for "-d=gossahash=..."
@@ -152,7 +157,11 @@ func NewHashDebug(ev, s string, file writeSyncer) *HashDebug {
 }
 
 func hashOf(pkgAndName string, param uint64) uint64 {
-       hbytes := notsha256.Sum256([]byte(pkgAndName))
+       return hashOfBytes([]byte(pkgAndName), param)
+}
+
+func hashOfBytes(sbytes []byte, param uint64) uint64 {
+       hbytes := notsha256.Sum256(sbytes)
        hash := uint64(hbytes[7])<<56 + uint64(hbytes[6])<<48 +
                uint64(hbytes[5])<<40 + uint64(hbytes[4])<<32 +
                uint64(hbytes[3])<<24 + uint64(hbytes[2])<<16 +
@@ -196,6 +205,7 @@ func (d *HashDebug) DebugHashMatchParam(pkgAndName string, param uint64) bool {
        if d.no {
                return false
        }
+
        if d.yes {
                d.logDebugHashMatch(d.name, pkgAndName, "y", param)
                return true
@@ -220,9 +230,71 @@ func (d *HashDebug) DebugHashMatchParam(pkgAndName string, param uint64) bool {
        return false
 }
 
+// DebugHashMatchPos is similar to DebugHashMatchParam, but for hash computation
+// it uses the source position including all inlining information instead of
+// package name and path. The output trigger string is prefixed with "POS=" so
+// that tools processing the output can reliably tell the difference. The mutex
+// locking is also more frequent and more granular.
+func (d *HashDebug) DebugHashMatchPos(ctxt *obj.Link, pos src.XPos) bool {
+       if d == nil {
+               return true
+       }
+       if d.no {
+               return false
+       }
+       d.mu.Lock()
+       defer d.mu.Unlock()
+
+       b := d.bytesForPos(ctxt, pos)
+
+       if d.yes {
+               d.logDebugHashMatchLocked(d.name, string(b), "y", 0)
+               return true
+       }
+
+       hash := hashOfBytes(b, 0)
+
+       for _, m := range d.matches {
+               if (m.hash^hash)&m.mask == 0 {
+                       hstr := ""
+                       if hash == 0 {
+                               hstr = "0"
+                       } else {
+                               for ; hash != 0; hash = hash >> 1 {
+                                       hstr = string('0'+byte(hash&1)) + hstr
+                               }
+                       }
+                       d.logDebugHashMatchLocked(m.name, "POS="+string(b), hstr, 0)
+                       return true
+               }
+       }
+       return false
+}
+
+// bytesForPos renders a position, including inlining, into d.bytesTmp
+// and returns the byte array.  d.mu must be locked.
+func (d *HashDebug) bytesForPos(ctxt *obj.Link, pos src.XPos) []byte {
+       d.posTmp = ctxt.AllPos(pos, d.posTmp)
+       // Reverse posTmp to put outermost first.
+       b := &d.bytesTmp
+       b.Reset()
+       for i := len(d.posTmp) - 1; i >= 0; i-- {
+               p := &d.posTmp[i]
+               fmt.Fprintf(b, "%s:%d:%d", p.Filename(), p.Line(), p.Col())
+               if i != 0 {
+                       b.WriteByte(';')
+               }
+       }
+       return b.Bytes()
+}
+
 func (d *HashDebug) logDebugHashMatch(varname, name, hstr string, param uint64) {
        d.mu.Lock()
        defer d.mu.Unlock()
+       d.logDebugHashMatchLocked(varname, name, hstr, param)
+}
+
+func (d *HashDebug) logDebugHashMatchLocked(varname, name, hstr string, param uint64) {
        file := d.logfile
        if file == nil {
                if tmpfile := os.Getenv("GSHS_LOGFILE"); tmpfile != "" {
index 78dd0baea29866f8559c818b2af1b251cbc8b826..1df6a63c2537f97e9a5ca9ceb8e43b9ecb2297cc 100644 (file)
@@ -9,8 +9,8 @@ import (
        "os"
        "os/exec"
        "path/filepath"
+       "regexp"
        "runtime"
-       "strings"
        "testing"
 )
 
@@ -18,9 +18,6 @@ import (
 // It does not check or run the generated code.
 // The test file is however a useful example of fused-vs-cascaded multiply-add.
 func TestFmaHash(t *testing.T) {
-       if testing.Short() {
-               t.Skip("Slow test, usually avoid it, testing.Short")
-       }
        switch runtime.GOOS {
        case "linux", "darwin":
        default:
@@ -42,7 +39,9 @@ func TestFmaHash(t *testing.T) {
        source := filepath.Join("testdata", "fma.go")
        output := filepath.Join(tmpdir, "fma.exe")
        cmd := exec.Command(gocmd, "build", "-o", output, source)
-       cmd.Env = append(cmd.Env, "GOCOMPILEDEBUG=fmahash=101111101101111001110110", "GOOS=linux", "GOARCH=arm64", "HOME="+tmpdir)
+       // The hash-dependence on file path name is dodged by specifying "all hashes ending in 1" plus "all hashes ending in 0"
+       // i.e., all hashes.  This will print all the FMAs; this test is only interested in one of them (that should appear near the end).
+       cmd.Env = append(cmd.Env, "GOCOMPILEDEBUG=fmahash=1/0", "GOOS=linux", "GOARCH=arm64", "HOME="+tmpdir)
        t.Logf("%v", cmd)
        t.Logf("%v", cmd.Env)
        b, e := cmd.CombinedOutput()
@@ -50,7 +49,9 @@ func TestFmaHash(t *testing.T) {
                t.Error(e)
        }
        s := string(b) // Looking for "GOFMAHASH triggered main.main:24"
-       if !strings.Contains(s, "fmahash triggered main.main:24") {
-               t.Errorf("Expected to see 'fmahash triggered main.main:24' in \n-----\n%s-----", s)
+       re := "fmahash(0?) triggered POS=.*fma.go:29:..;.*fma.go:18:.."
+       match := regexp.MustCompile(re)
+       if !match.MatchString(s) {
+               t.Errorf("Expected to match '%s' with \n-----\n%s-----", re, s)
        }
 }
index 18226c42b93dd197d91abaca607e743b6e8ef3b5..c988461a40381ff461071e772445a38282fbcc31 100644 (file)
@@ -811,7 +811,6 @@ func (f *Func) useFMA(v *Value) bool {
        if base.FmaHash == nil {
                return true
        }
-
-       name := f.fe.MyImportPath() + "." + f.Name
-       return base.FmaHash.DebugHashMatchParam(name, uint64(v.Pos.Line()))
+       ctxt := v.Block.Func.Config.Ctxt()
+       return base.FmaHash.DebugHashMatchPos(ctxt, v.Pos)
 }
index 468448b9e669880fd97db894646c60895c242c87..13a7ff1e1cb2a1aa886d74022d3bf323304d1fa8 100644 (file)
@@ -14,6 +14,10 @@ func f(x float64) float64 {
        return x
 }
 
+func inlineFma(x, y, z float64) float64 {
+       return x + y*z
+}
+
 func main() {
        w, x, y := 1.0, 1.0, 1.0
        x = f(x + x/(1<<52))
@@ -21,7 +25,9 @@ func main() {
        y = f(y + y/(1<<52))
        w0 := f(2 * w * (1 - w))
        w1 := f(w * (1 + w))
-       x = x + w0*w1 // GOFMAHASH=101111101101111001110110
+       x = x + w0*w1
+       x = inlineFma(x, w0, w1)
+       y = y + f(w0*w1)
        y = y + f(w0*w1)
        fmt.Println(x, y, x-y)