mirror of https://github.com/golang/go.git
cmd/compile: add ability to hash-debug on file:line, including inlining
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>
This commit is contained in:
parent
271f139f17
commit
d33043d37d
|
|
@ -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 != "" {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue