[dev.link] all: merge branch 'master' into dev.link

Clean merge.

Change-Id: I2ae070c60c011779a0f0a1344f5b6d45ef10d8a1
This commit is contained in:
Cherry Zhang 2020-03-13 14:45:05 -04:00
commit 440852c464
118 changed files with 5302 additions and 12481 deletions

View File

@ -183,6 +183,29 @@ func IndexAny(s []byte, chars string) int {
// Avoid scanning all of s. // Avoid scanning all of s.
return -1 return -1
} }
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
// search utf8.RuneError.
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
if len(chars) == 1 {
r := rune(chars[0])
if r >= utf8.RuneSelf {
r = utf8.RuneError
}
return IndexRune(s, r)
}
if len(s) > 8 { if len(s) > 8 {
if as, isASCII := makeASCIISet(chars); isASCII { if as, isASCII := makeASCIISet(chars); isASCII {
for i, c := range s { for i, c := range s {
@ -197,14 +220,26 @@ func IndexAny(s []byte, chars string) int {
for i := 0; i < len(s); i += width { for i := 0; i < len(s); i += width {
r := rune(s[i]) r := rune(s[i])
if r < utf8.RuneSelf { if r < utf8.RuneSelf {
width = 1 if bytealg.IndexByteString(chars, s[i]) >= 0 {
} else {
r, width = utf8.DecodeRune(s[i:])
}
for _, ch := range chars {
if r == ch {
return i return i
} }
width = 1
continue
}
r, width = utf8.DecodeRune(s[i:])
if r == utf8.RuneError {
for _, r = range chars {
if r == utf8.RuneError {
return i
}
}
continue
}
// r is 2 to 4 bytes. Using strings.Index is more reasonable, but as the bytes
// package should not import the strings package, use bytealg.IndexString
// instead. And this does not seem to lose much performance.
if chars == string(r) || bytealg.IndexString(chars, string(r)) >= 0 {
return i
} }
} }
return -1 return -1
@ -229,14 +264,60 @@ func LastIndexAny(s []byte, chars string) int {
return -1 return -1
} }
} }
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
if len(chars) == 1 {
cr := rune(chars[0])
if cr >= utf8.RuneSelf {
cr = utf8.RuneError
}
for i := len(s); i > 0; { for i := len(s); i > 0; {
r, size := utf8.DecodeLastRune(s[:i]) r, size := utf8.DecodeLastRune(s[:i])
i -= size i -= size
for _, c := range chars { if r == cr {
if r == c {
return i return i
} }
} }
return -1
}
for i := len(s); i > 0; {
r := rune(s[i-1])
if r < utf8.RuneSelf {
if bytealg.IndexByteString(chars, s[i-1]) >= 0 {
return i - 1
}
i--
continue
}
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r == utf8.RuneError {
for _, r = range chars {
if r == utf8.RuneError {
return i
}
}
continue
}
// r is 2 to 4 bytes. Using strings.Index is more reasonable, but as the bytes
// package should not import the strings package, use bytealg.IndexString
// instead. And this does not seem to lose much performance.
if chars == string(r) || bytealg.IndexString(chars, string(r)) >= 0 {
return i
}
} }
return -1 return -1
} }

View File

@ -169,6 +169,7 @@ var indexAnyTests = []BinOpTest{
{"", "abc", -1}, {"", "abc", -1},
{"a", "", -1}, {"a", "", -1},
{"a", "a", 0}, {"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 0}, {"aaa", "a", 0},
{"abc", "xyz", -1}, {"abc", "xyz", -1},
{"abc", "xcz", 2}, {"abc", "xcz", 2},
@ -179,6 +180,7 @@ var indexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1}, {dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 4}, {"012abcba210", "\xffb", 4},
{"012\x80bcb\x80210", "\xffb", 3}, {"012\x80bcb\x80210", "\xffb", 3},
{"0123456\xcf\x80abc", "\xcfb\x80", 10},
} }
var lastIndexAnyTests = []BinOpTest{ var lastIndexAnyTests = []BinOpTest{
@ -187,6 +189,7 @@ var lastIndexAnyTests = []BinOpTest{
{"", "abc", -1}, {"", "abc", -1},
{"a", "", -1}, {"a", "", -1},
{"a", "a", 0}, {"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 2}, {"aaa", "a", 2},
{"abc", "xyz", -1}, {"abc", "xyz", -1},
{"abc", "ab", 1}, {"abc", "ab", 1},
@ -197,6 +200,7 @@ var lastIndexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1}, {dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 6}, {"012abcba210", "\xffb", 6},
{"012\x80bcb\x80210", "\xffb", 7}, {"012\x80bcb\x80210", "\xffb", 7},
{"0123456\xcf\x80abc", "\xcfb\x80", 10},
} }
// Execute f on each test case. funcName should be the name of f; it's used // Execute f on each test case. funcName should be the name of f; it's used
@ -1890,10 +1894,10 @@ func BenchmarkBytesCompare(b *testing.B) {
} }
func BenchmarkIndexAnyASCII(b *testing.B) { func BenchmarkIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 4096) // Never matches set x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "0123456789abcdef" cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
for k := 1; k <= 4096; k <<= 4 { for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 16; j <<= 1 { for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) { b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
IndexAny(x[:k], cs[:j]) IndexAny(x[:k], cs[:j])
@ -1903,6 +1907,48 @@ func BenchmarkIndexAnyASCII(b *testing.B) {
} }
} }
func BenchmarkIndexAnyUTF8(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
IndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkLastIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
LastIndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkLastIndexAnyUTF8(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
LastIndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkTrimASCII(b *testing.B) { func BenchmarkTrimASCII(b *testing.B) {
cs := "0123456789abcdef" cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 { for k := 1; k <= 4096; k <<= 4 {

View File

@ -1064,7 +1064,7 @@ label1:
// VSX AND, XX3-form // VSX AND, XX3-form
// <MNEMONIC> XA,XB,XT produces // <MNEMONIC> XA,XB,XT produces
// <mnemonic> XT,XA,XB // <mnemonic> XT,XA,XB
XXLANDQ VS0,VS1,VS32 XXLAND VS0,VS1,VS32
XXLANDC VS0,VS1,VS32 XXLANDC VS0,VS1,VS32
XXLEQV VS0,VS1,VS32 XXLEQV VS0,VS1,VS32
XXLNAND VS0,VS1,VS32 XXLNAND VS0,VS1,VS32
@ -1093,6 +1093,11 @@ label1:
// <mnemonic> XT,XB,UIM // <mnemonic> XT,XB,UIM
XXSPLTW VS0,$3,VS32 XXSPLTW VS0,$3,VS32
// VSX permute, XX3-form
// <MNEMONIC> XA,XB,XT produces
// <mnemonic> XT,XA,XB
XXPERM VS0,VS1,VS32
// VSX permute, XX3-form // VSX permute, XX3-form
// <MNEMONIC> XA,XB,DM,XT produces // <MNEMONIC> XA,XB,DM,XT produces
// <mnemonic> XT,XA,XB,DM // <mnemonic> XT,XA,XB,DM

View File

@ -22,6 +22,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"unicode"
) )
var ( var (
@ -802,6 +803,28 @@ func (p *Package) packedAttribute() string {
return s + "))" return s + "))"
} }
// exportParamName returns the value of param as it should be
// displayed in a c header file. If param contains any non-ASCII
// characters, this function will return the character p followed by
// the value of position; otherwise, this function will return the
// value of param.
func exportParamName(param string, position int) string {
if param == "" {
return fmt.Sprintf("p%d", position)
}
pname := param
for i := 0; i < len(param); i++ {
if param[i] > unicode.MaxASCII {
pname = fmt.Sprintf("p%d", position)
break
}
}
return pname
}
// Write out the various stubs we need to support functions exported // Write out the various stubs we need to support functions exported
// from Go so that they are callable from C. // from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
@ -915,42 +938,45 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
if i > 0 || fn.Recv != nil { if i > 0 || fn.Recv != nil {
s += ", " s += ", "
} }
s += fmt.Sprintf("%s p%d", p.cgoType(atype).C, i) s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i))
}) })
s += ")" s += ")"
if len(exp.Doc) > 0 { if len(exp.Doc) > 0 {
fmt.Fprintf(fgcch, "\n%s", exp.Doc) fmt.Fprintf(fgcch, "\n%s", exp.Doc)
if !strings.HasSuffix(exp.Doc, "\n") {
fmt.Fprint(fgcch, "\n")
} }
fmt.Fprintf(fgcch, "\nextern %s;\n", s) }
fmt.Fprintf(fgcch, "extern %s;\n", s)
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD")
fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "\n%s\n", s)
fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n") fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "\t%s %v a;\n", ctype, p.packedAttribute()) fmt.Fprintf(fgcc, "\t%s %v _cgo_a;\n", ctype, p.packedAttribute())
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
} }
if fn.Recv != nil { if fn.Recv != nil {
fmt.Fprintf(fgcc, "\ta.recv = recv;\n") fmt.Fprintf(fgcc, "\t_cgo_a.recv = recv;\n")
} }
forFieldList(fntype.Params, forFieldList(fntype.Params,
func(i int, aname string, atype ast.Expr) { func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i) fmt.Fprintf(fgcc, "\t_cgo_a.p%d = %s;\n", i, exportParamName(aname, i))
}) })
fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n") fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off) fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &_cgo_a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n") fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
fmt.Fprintf(fgcc, "\t_cgo_release_context(_cgo_ctxt);\n") fmt.Fprintf(fgcc, "\t_cgo_release_context(_cgo_ctxt);\n")
if gccResult != "void" { if gccResult != "void" {
if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
fmt.Fprintf(fgcc, "\treturn a.r0;\n") fmt.Fprintf(fgcc, "\treturn _cgo_a.r0;\n")
} else { } else {
forFieldList(fntype.Results, forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) { func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\tr.r%d = a.r%d;\n", i, i) fmt.Fprintf(fgcc, "\tr.r%d = _cgo_a.r%d;\n", i, i)
}) })
fmt.Fprintf(fgcc, "\treturn r;\n") fmt.Fprintf(fgcc, "\treturn r;\n")
} }

View File

@ -77,8 +77,6 @@ Flags:
-lang version -lang version
Set language version to compile, as in -lang=go1.12. Set language version to compile, as in -lang=go1.12.
Default is current version. Default is current version.
-largemodel
Generate code that assumes a large memory model.
-linkobj file -linkobj file
Write linker-specific object to file and compiler-specific Write linker-specific object to file and compiler-specific
object to usual output file (as specified by -o). object to usual output file (as specified by -o).

View File

@ -186,6 +186,7 @@ func algtype1(t *types.Type) (AlgKind, *types.Type) {
// genhash returns a symbol which is the closure used to compute // genhash returns a symbol which is the closure used to compute
// the hash of a value of type t. // the hash of a value of type t.
// Note: the generated function must match runtime.typehash exactly.
func genhash(t *types.Type) *obj.LSym { func genhash(t *types.Type) *obj.LSym {
switch algtype(t) { switch algtype(t) {
default: default:

View File

@ -673,10 +673,6 @@ func evconst(n *Node) {
} }
case OCOMPLEX: case OCOMPLEX:
if nl == nil || nr == nil {
// TODO(mdempsky): Remove after early OAS2FUNC rewrite CL lands.
break
}
if nl.Op == OLITERAL && nr.Op == OLITERAL { if nl.Op == OLITERAL && nr.Op == OLITERAL {
// make it a complex literal // make it a complex literal
c := newMpcmplx() c := newMpcmplx()

View File

@ -1371,7 +1371,7 @@ func (e *Escape) finish(fns []*Node) {
fn.Esc = EscFuncTagged fn.Esc = EscFuncTagged
narg := 0 narg := 0
for _, fs := range types.RecvsParams { for _, fs := range &types.RecvsParams {
for _, f := range fs(fn.Type).Fields().Slice() { for _, f := range fs(fn.Type).Fields().Slice() {
narg++ narg++
f.Note = e.paramTag(fn, narg, f) f.Note = e.paramTag(fn, narg, f)

View File

@ -585,28 +585,44 @@ s%^ ........*\]%&~%g
s%~ %%g s%~ %%g
*/ */
func symfmt(s *types.Sym, flag FmtFlag, mode fmtMode) string { func symfmt(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode fmtMode) {
if s.Pkg != nil && flag&FmtShort == 0 { if s.Pkg != nil && flag&FmtShort == 0 {
switch mode { switch mode {
case FErr: // This is for the user case FErr: // This is for the user
if s.Pkg == builtinpkg || s.Pkg == localpkg { if s.Pkg == builtinpkg || s.Pkg == localpkg {
return s.Name b.WriteString(s.Name)
return
} }
// If the name was used by multiple packages, display the full path, // If the name was used by multiple packages, display the full path,
if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 { if s.Pkg.Name != "" && numImport[s.Pkg.Name] > 1 {
return fmt.Sprintf("%q.%s", s.Pkg.Path, s.Name) fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
return
} }
return s.Pkg.Name + "." + s.Name b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
case FDbg: case FDbg:
return s.Pkg.Name + "." + s.Name b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
case FTypeIdName: case FTypeIdName:
return s.Pkg.Name + "." + s.Name // dcommontype, typehash // dcommontype, typehash
b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
case FTypeId: case FTypeId:
return s.Pkg.Prefix + "." + s.Name // (methodsym), typesym, weaksym // (methodsym), typesym, weaksym
b.WriteString(s.Pkg.Prefix)
b.WriteByte('.')
b.WriteString(s.Name)
return
} }
} }
@ -619,13 +635,15 @@ func symfmt(s *types.Sym, flag FmtFlag, mode fmtMode) string {
} }
if mode == FDbg { if mode == FDbg {
return fmt.Sprintf("@%q.%s", s.Pkg.Path, name) fmt.Fprintf(b, "@%q.%s", s.Pkg.Path, name)
return
} }
return name b.WriteString(name)
return
} }
return s.Name b.WriteString(s.Name)
} }
var basicnames = []string{ var basicnames = []string{
@ -652,16 +670,16 @@ var basicnames = []string{
TBLANK: "blank", TBLANK: "blank",
} }
var tconvBufferPool = sync.Pool{ var fmtBufferPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return new(bytes.Buffer) return new(bytes.Buffer)
}, },
} }
func tconv(t *types.Type, flag FmtFlag, mode fmtMode) string { func tconv(t *types.Type, flag FmtFlag, mode fmtMode) string {
buf := tconvBufferPool.Get().(*bytes.Buffer) buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset() buf.Reset()
defer tconvBufferPool.Put(buf) defer fmtBufferPool.Put(buf)
tconv2(buf, t, flag, mode, nil) tconv2(buf, t, flag, mode, nil)
return types.InternString(buf.Bytes()) return types.InternString(buf.Bytes())
@ -703,7 +721,7 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
case FTypeIdName, FTypeId: case FTypeIdName, FTypeId:
t = types.Types[t.Etype] t = types.Types[t.Etype]
default: default:
b.WriteString(sconv(t.Sym, FmtShort, mode)) sconv2(b, t.Sym, FmtShort, mode)
return return
} }
} }
@ -718,15 +736,16 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
case FTypeId, FTypeIdName: case FTypeId, FTypeIdName:
if flag&FmtShort != 0 { if flag&FmtShort != 0 {
if t.Vargen != 0 { if t.Vargen != 0 {
fmt.Fprintf(b, "%s·%d", sconv(t.Sym, FmtShort, mode), t.Vargen) sconv2(b, t.Sym, FmtShort, mode)
fmt.Fprintf(b, "·%d", t.Vargen)
return return
} }
b.WriteString(sconv(t.Sym, FmtShort, mode)) sconv2(b, t.Sym, FmtShort, mode)
return return
} }
if mode == FTypeIdName { if mode == FTypeIdName {
b.WriteString(sconv(t.Sym, FmtUnsigned, mode)) sconv2(b, t.Sym, FmtUnsigned, mode)
return return
} }
@ -736,7 +755,7 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
} }
} }
b.WriteString(smodeString(t.Sym, mode)) sconv2(b, t.Sym, 0, mode)
return return
} }
@ -845,13 +864,13 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
// Wrong interface definitions may have types lacking a symbol. // Wrong interface definitions may have types lacking a symbol.
break break
case types.IsExported(f.Sym.Name): case types.IsExported(f.Sym.Name):
b.WriteString(sconv(f.Sym, FmtShort, mode)) sconv2(b, f.Sym, FmtShort, mode)
default: default:
flag1 := FmtLeft flag1 := FmtLeft
if flag&FmtUnsigned != 0 { if flag&FmtUnsigned != 0 {
flag1 = FmtUnsigned flag1 = FmtUnsigned
} }
b.WriteString(sconv(f.Sym, flag1, mode)) sconv2(b, f.Sym, flag1, mode)
} }
tconv2(b, f.Type, FmtShort, mode, visited) tconv2(b, f.Type, FmtShort, mode, visited)
} }
@ -941,7 +960,7 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
b.WriteString("undefined") b.WriteString("undefined")
if t.Sym != nil { if t.Sym != nil {
b.WriteByte(' ') b.WriteByte(' ')
b.WriteString(smodeString(t.Sym, mode)) sconv2(b, t.Sym, 0, mode)
} }
case TUNSAFEPTR: case TUNSAFEPTR:
@ -1731,9 +1750,30 @@ func sconv(s *types.Sym, flag FmtFlag, mode fmtMode) string {
if s.Name == "_" { if s.Name == "_" {
return "_" return "_"
} }
buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer fmtBufferPool.Put(buf)
flag, mode = flag.update(mode) flag, mode = flag.update(mode)
return symfmt(s, flag, mode) symfmt(buf, s, flag, mode)
return types.InternString(buf.Bytes())
}
func sconv2(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode fmtMode) {
if flag&FmtLong != 0 {
panic("linksymfmt")
}
if s == nil {
b.WriteString("<S>")
return
}
if s.Name == "_" {
b.WriteString("_")
return
}
flag, mode = flag.update(mode)
symfmt(b, s, flag, mode)
} }
func fldconv(b *bytes.Buffer, f *types.Field, flag FmtFlag, mode fmtMode, visited map[*types.Type]int, funarg types.Funarg) { func fldconv(b *bytes.Buffer, f *types.Field, flag FmtFlag, mode fmtMode, visited map[*types.Type]int, funarg types.Funarg) {

View File

@ -954,7 +954,7 @@ func (w *exportWriter) funcExt(n *Node) {
w.symIdx(n.Sym) w.symIdx(n.Sym)
// Escape analysis. // Escape analysis.
for _, fs := range types.RecvsParams { for _, fs := range &types.RecvsParams {
for _, f := range fs(n.Type).FieldSlice() { for _, f := range fs(n.Type).FieldSlice() {
w.string(f.Note) w.string(f.Note)
} }

View File

@ -660,7 +660,7 @@ func (r *importReader) funcExt(n *Node) {
r.symIdx(n.Sym) r.symIdx(n.Sym)
// Escape analysis. // Escape analysis.
for _, fs := range types.RecvsParams { for _, fs := range &types.RecvsParams {
for _, f := range fs(n.Type).FieldSlice() { for _, f := range fs(n.Type).FieldSlice() {
f.Note = r.string() f.Note = r.string()
} }

View File

@ -1070,7 +1070,7 @@ func loadsys() {
typecheckok = true typecheckok = true
typs := runtimeTypes() typs := runtimeTypes()
for _, d := range runtimeDecls { for _, d := range &runtimeDecls {
sym := Runtimepkg.Lookup(d.name) sym := Runtimepkg.Lookup(d.name)
typ := typs[d.typ] typ := typs[d.typ]
switch d.tag { switch d.tag {
@ -1373,7 +1373,7 @@ var concurrentFlagOK = [256]bool{
} }
func concurrentBackendAllowed() bool { func concurrentBackendAllowed() bool {
for i, x := range Debug { for i, x := range &Debug {
if x != 0 && !concurrentFlagOK[i] { if x != 0 && !concurrentFlagOK[i] {
return false return false
} }

View File

@ -646,7 +646,7 @@ func (p *noder) expr(expr syntax.Expr) *Node {
} }
n := p.nod(expr, op, p.expr(expr.X), nil) n := p.nod(expr, op, p.expr(expr.X), nil)
var index [3]*Node var index [3]*Node
for i, x := range expr.Index { for i, x := range &expr.Index {
if x != nil { if x != nil {
index[i] = p.expr(x) index[i] = p.expr(x)
} }

View File

@ -419,7 +419,7 @@ func (lv *Liveness) regEffects(v *ssa.Value) (uevar, kill liveRegMask) {
if v.Type.Etype != types.TTUPLE { if v.Type.Etype != types.TTUPLE {
v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type) v.Fatalf("location pair %s has non-tuple type %v", loc, v.Type)
} }
for i, loc1 := range loc { for i, loc1 := range &loc {
if loc1 == nil { if loc1 == nil {
continue continue
} }
@ -705,12 +705,6 @@ func (lv *Liveness) markUnsafePoints() {
v = v.Args[0] v = v.Args[0]
continue continue
} }
case ssa.OpRISCV64SUBW:
// RISCV64 lowers Neq32 to include a SUBW with multiple arguments.
// TODO(jsing): it would be preferable not to use Neq32 for
// writeBuffer.enabled checks on this platform.
v = v.Args[0]
continue
case ssa.Op386MOVLload, ssa.OpARM64MOVWUload, ssa.OpPPC64MOVWZload, ssa.OpWasmI64Load32U: case ssa.Op386MOVLload, ssa.OpARM64MOVWUload, ssa.OpPPC64MOVWZload, ssa.OpWasmI64Load32U:
// Args[0] is the address of the write // Args[0] is the address of the write
// barrier control. Ignore Args[1], // barrier control. Ignore Args[1],

View File

@ -3216,7 +3216,7 @@ func init() {
var p4 []*sys.Arch var p4 []*sys.Arch
var p8 []*sys.Arch var p8 []*sys.Arch
var lwatomics []*sys.Arch var lwatomics []*sys.Arch
for _, a := range sys.Archs { for _, a := range &sys.Archs {
all = append(all, a) all = append(all, a)
if a.PtrSize == 4 { if a.PtrSize == 4 {
p4 = append(p4, a) p4 = append(p4, a)

View File

@ -66,7 +66,7 @@ var builtinFuncs = [...]struct {
// isBuiltinFuncName reports whether name matches a builtin function // isBuiltinFuncName reports whether name matches a builtin function
// name. // name.
func isBuiltinFuncName(name string) bool { func isBuiltinFuncName(name string) bool {
for _, fn := range builtinFuncs { for _, fn := range &builtinFuncs {
if fn.name == name { if fn.name == name {
return true return true
} }
@ -92,7 +92,7 @@ func initUniverse() {
// lexinit initializes known symbols and the basic types. // lexinit initializes known symbols and the basic types.
func lexinit() { func lexinit() {
for _, s := range basicTypes { for _, s := range &basicTypes {
etype := s.etype etype := s.etype
if int(etype) >= len(types.Types) { if int(etype) >= len(types.Types) {
Fatalf("lexinit: %s bad etype", s.name) Fatalf("lexinit: %s bad etype", s.name)
@ -111,13 +111,13 @@ func lexinit() {
asNode(s2.Def).Name = new(Name) asNode(s2.Def).Name = new(Name)
} }
for _, s := range builtinFuncs { for _, s := range &builtinFuncs {
s2 := builtinpkg.Lookup(s.name) s2 := builtinpkg.Lookup(s.name)
s2.Def = asTypesNode(newname(s2)) s2.Def = asTypesNode(newname(s2))
asNode(s2.Def).SetSubOp(s.op) asNode(s2.Def).SetSubOp(s.op)
} }
for _, s := range unsafeFuncs { for _, s := range &unsafeFuncs {
s2 := unsafepkg.Lookup(s.name) s2 := unsafepkg.Lookup(s.name)
s2.Def = asTypesNode(newname(s2)) s2.Def = asTypesNode(newname(s2))
asNode(s2.Def).SetSubOp(s.op) asNode(s2.Def).SetSubOp(s.op)
@ -402,7 +402,7 @@ func lexinit1() {
dowidth(types.Runetype) dowidth(types.Runetype)
// backend-dependent builtin types (e.g. int). // backend-dependent builtin types (e.g. int).
for _, s := range typedefs { for _, s := range &typedefs {
s1 := builtinpkg.Lookup(s.name) s1 := builtinpkg.Lookup(s.name)
sameas := s.sameas32 sameas := s.sameas32

View File

@ -390,7 +390,7 @@ func FlushLoggedOpts(ctxt *obj.Link, slashPkgPath string) {
var w io.WriteCloser var w io.WriteCloser
if slashPkgPath == "" { if slashPkgPath == "" {
slashPkgPath = string(0) slashPkgPath = "\000"
} }
subdirpath := filepath.Join(dest, pathEscape(slashPkgPath)) subdirpath := filepath.Join(dest, pathEscape(slashPkgPath))
err := os.MkdirAll(subdirpath, 0755) err := os.MkdirAll(subdirpath, 0755)

View File

@ -0,0 +1,225 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// addressingModes combines address calculations into memory operations
// that can perform complicated addressing modes.
func addressingModes(f *Func) {
switch f.Config.arch {
default:
// Most architectures can't do this.
return
case "amd64", "386":
// TODO: s390x?
}
var tmp []*Value
for _, b := range f.Blocks {
for _, v := range b.Values {
if !combineFirst[v.Op] {
continue
}
// All matched operations have the pointer in arg[0].
// All results have the pointer in arg[0] and the index in arg[1].
// *Except* for operations which update a register,
// which are marked with resultInArg0. Those have
// the pointer in arg[1], and the corresponding result op
// has the pointer in arg[1] and the index in arg[2].
ptrIndex := 0
if opcodeTable[v.Op].resultInArg0 {
ptrIndex = 1
}
p := v.Args[ptrIndex]
c, ok := combine[[2]Op{v.Op, p.Op}]
if !ok {
continue
}
// See if we can combine the Aux/AuxInt values.
switch [2]auxType{opcodeTable[v.Op].auxType, opcodeTable[p.Op].auxType} {
case [2]auxType{auxSymOff, auxInt32}:
// TODO: introduce auxSymOff32
if !is32Bit(v.AuxInt + p.AuxInt) {
continue
}
v.AuxInt += p.AuxInt
case [2]auxType{auxSymOff, auxSymOff}:
if v.Aux != nil && p.Aux != nil {
continue
}
if !is32Bit(v.AuxInt + p.AuxInt) {
continue
}
if p.Aux != nil {
v.Aux = p.Aux
}
v.AuxInt += p.AuxInt
case [2]auxType{auxSymValAndOff, auxInt32}:
vo := ValAndOff(v.AuxInt)
if !vo.canAdd(p.AuxInt) {
continue
}
v.AuxInt = vo.add(p.AuxInt)
case [2]auxType{auxSymValAndOff, auxSymOff}:
vo := ValAndOff(v.AuxInt)
if v.Aux != nil && p.Aux != nil {
continue
}
if !vo.canAdd(p.AuxInt) {
continue
}
if p.Aux != nil {
v.Aux = p.Aux
}
v.AuxInt = vo.add(p.AuxInt)
case [2]auxType{auxSymOff, auxNone}:
// nothing to do
case [2]auxType{auxSymValAndOff, auxNone}:
// nothing to do
default:
f.Fatalf("unknown aux combining for %s and %s\n", v.Op, p.Op)
}
// Combine the operations.
tmp = append(tmp[:0], v.Args[:ptrIndex]...)
tmp = append(tmp, p.Args...)
tmp = append(tmp, v.Args[ptrIndex+1:]...)
v.resetArgs()
v.Op = c
v.AddArgs(tmp...)
}
}
}
// combineFirst contains ops which appear in combine as the
// first part of the key.
var combineFirst = map[Op]bool{}
func init() {
for k := range combine {
combineFirst[k[0]] = true
}
}
// For each entry k, v in this map, if we have a value x with:
// x.Op == k[0]
// x.Args[0].Op == k[1]
// then we can set x.Op to v and set x.Args like this:
// x.Args[0].Args + x.Args[1:]
// Additionally, the Aux/AuxInt from x.Args[0] is merged into x.
var combine = map[[2]Op]Op{
// amd64
[2]Op{OpAMD64MOVBload, OpAMD64ADDQ}: OpAMD64MOVBloadidx1,
[2]Op{OpAMD64MOVWload, OpAMD64ADDQ}: OpAMD64MOVWloadidx1,
[2]Op{OpAMD64MOVLload, OpAMD64ADDQ}: OpAMD64MOVLloadidx1,
[2]Op{OpAMD64MOVQload, OpAMD64ADDQ}: OpAMD64MOVQloadidx1,
[2]Op{OpAMD64MOVSSload, OpAMD64ADDQ}: OpAMD64MOVSSloadidx1,
[2]Op{OpAMD64MOVSDload, OpAMD64ADDQ}: OpAMD64MOVSDloadidx1,
[2]Op{OpAMD64MOVBstore, OpAMD64ADDQ}: OpAMD64MOVBstoreidx1,
[2]Op{OpAMD64MOVWstore, OpAMD64ADDQ}: OpAMD64MOVWstoreidx1,
[2]Op{OpAMD64MOVLstore, OpAMD64ADDQ}: OpAMD64MOVLstoreidx1,
[2]Op{OpAMD64MOVQstore, OpAMD64ADDQ}: OpAMD64MOVQstoreidx1,
[2]Op{OpAMD64MOVSSstore, OpAMD64ADDQ}: OpAMD64MOVSSstoreidx1,
[2]Op{OpAMD64MOVSDstore, OpAMD64ADDQ}: OpAMD64MOVSDstoreidx1,
[2]Op{OpAMD64MOVBstoreconst, OpAMD64ADDQ}: OpAMD64MOVBstoreconstidx1,
[2]Op{OpAMD64MOVWstoreconst, OpAMD64ADDQ}: OpAMD64MOVWstoreconstidx1,
[2]Op{OpAMD64MOVLstoreconst, OpAMD64ADDQ}: OpAMD64MOVLstoreconstidx1,
[2]Op{OpAMD64MOVQstoreconst, OpAMD64ADDQ}: OpAMD64MOVQstoreconstidx1,
[2]Op{OpAMD64MOVBload, OpAMD64LEAQ1}: OpAMD64MOVBloadidx1,
[2]Op{OpAMD64MOVWload, OpAMD64LEAQ1}: OpAMD64MOVWloadidx1,
[2]Op{OpAMD64MOVWload, OpAMD64LEAQ2}: OpAMD64MOVWloadidx2,
[2]Op{OpAMD64MOVLload, OpAMD64LEAQ1}: OpAMD64MOVLloadidx1,
[2]Op{OpAMD64MOVLload, OpAMD64LEAQ4}: OpAMD64MOVLloadidx4,
[2]Op{OpAMD64MOVLload, OpAMD64LEAQ8}: OpAMD64MOVLloadidx8,
[2]Op{OpAMD64MOVQload, OpAMD64LEAQ1}: OpAMD64MOVQloadidx1,
[2]Op{OpAMD64MOVQload, OpAMD64LEAQ8}: OpAMD64MOVQloadidx8,
[2]Op{OpAMD64MOVSSload, OpAMD64LEAQ1}: OpAMD64MOVSSloadidx1,
[2]Op{OpAMD64MOVSSload, OpAMD64LEAQ4}: OpAMD64MOVSSloadidx4,
[2]Op{OpAMD64MOVSDload, OpAMD64LEAQ1}: OpAMD64MOVSDloadidx1,
[2]Op{OpAMD64MOVSDload, OpAMD64LEAQ8}: OpAMD64MOVSDloadidx8,
[2]Op{OpAMD64MOVBstore, OpAMD64LEAQ1}: OpAMD64MOVBstoreidx1,
[2]Op{OpAMD64MOVWstore, OpAMD64LEAQ1}: OpAMD64MOVWstoreidx1,
[2]Op{OpAMD64MOVWstore, OpAMD64LEAQ2}: OpAMD64MOVWstoreidx2,
[2]Op{OpAMD64MOVLstore, OpAMD64LEAQ1}: OpAMD64MOVLstoreidx1,
[2]Op{OpAMD64MOVLstore, OpAMD64LEAQ4}: OpAMD64MOVLstoreidx4,
[2]Op{OpAMD64MOVLstore, OpAMD64LEAQ8}: OpAMD64MOVLstoreidx8,
[2]Op{OpAMD64MOVQstore, OpAMD64LEAQ1}: OpAMD64MOVQstoreidx1,
[2]Op{OpAMD64MOVQstore, OpAMD64LEAQ8}: OpAMD64MOVQstoreidx8,
[2]Op{OpAMD64MOVSSstore, OpAMD64LEAQ1}: OpAMD64MOVSSstoreidx1,
[2]Op{OpAMD64MOVSSstore, OpAMD64LEAQ4}: OpAMD64MOVSSstoreidx4,
[2]Op{OpAMD64MOVSDstore, OpAMD64LEAQ1}: OpAMD64MOVSDstoreidx1,
[2]Op{OpAMD64MOVSDstore, OpAMD64LEAQ8}: OpAMD64MOVSDstoreidx8,
[2]Op{OpAMD64MOVBstoreconst, OpAMD64LEAQ1}: OpAMD64MOVBstoreconstidx1,
[2]Op{OpAMD64MOVWstoreconst, OpAMD64LEAQ1}: OpAMD64MOVWstoreconstidx1,
[2]Op{OpAMD64MOVWstoreconst, OpAMD64LEAQ2}: OpAMD64MOVWstoreconstidx2,
[2]Op{OpAMD64MOVLstoreconst, OpAMD64LEAQ1}: OpAMD64MOVLstoreconstidx1,
[2]Op{OpAMD64MOVLstoreconst, OpAMD64LEAQ4}: OpAMD64MOVLstoreconstidx4,
[2]Op{OpAMD64MOVQstoreconst, OpAMD64LEAQ1}: OpAMD64MOVQstoreconstidx1,
[2]Op{OpAMD64MOVQstoreconst, OpAMD64LEAQ8}: OpAMD64MOVQstoreconstidx8,
// 386
[2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1,
[2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1,
[2]Op{Op386MOVLload, Op386ADDL}: Op386MOVLloadidx1,
[2]Op{Op386MOVSSload, Op386ADDL}: Op386MOVSSloadidx1,
[2]Op{Op386MOVSDload, Op386ADDL}: Op386MOVSDloadidx1,
[2]Op{Op386MOVBstore, Op386ADDL}: Op386MOVBstoreidx1,
[2]Op{Op386MOVWstore, Op386ADDL}: Op386MOVWstoreidx1,
[2]Op{Op386MOVLstore, Op386ADDL}: Op386MOVLstoreidx1,
[2]Op{Op386MOVSSstore, Op386ADDL}: Op386MOVSSstoreidx1,
[2]Op{Op386MOVSDstore, Op386ADDL}: Op386MOVSDstoreidx1,
[2]Op{Op386MOVBstoreconst, Op386ADDL}: Op386MOVBstoreconstidx1,
[2]Op{Op386MOVWstoreconst, Op386ADDL}: Op386MOVWstoreconstidx1,
[2]Op{Op386MOVLstoreconst, Op386ADDL}: Op386MOVLstoreconstidx1,
[2]Op{Op386MOVBload, Op386LEAL1}: Op386MOVBloadidx1,
[2]Op{Op386MOVWload, Op386LEAL1}: Op386MOVWloadidx1,
[2]Op{Op386MOVWload, Op386LEAL2}: Op386MOVWloadidx2,
[2]Op{Op386MOVLload, Op386LEAL1}: Op386MOVLloadidx1,
[2]Op{Op386MOVLload, Op386LEAL4}: Op386MOVLloadidx4,
[2]Op{Op386MOVSSload, Op386LEAL1}: Op386MOVSSloadidx1,
[2]Op{Op386MOVSSload, Op386LEAL4}: Op386MOVSSloadidx4,
[2]Op{Op386MOVSDload, Op386LEAL1}: Op386MOVSDloadidx1,
[2]Op{Op386MOVSDload, Op386LEAL8}: Op386MOVSDloadidx8,
[2]Op{Op386MOVBstore, Op386LEAL1}: Op386MOVBstoreidx1,
[2]Op{Op386MOVWstore, Op386LEAL1}: Op386MOVWstoreidx1,
[2]Op{Op386MOVWstore, Op386LEAL2}: Op386MOVWstoreidx2,
[2]Op{Op386MOVLstore, Op386LEAL1}: Op386MOVLstoreidx1,
[2]Op{Op386MOVLstore, Op386LEAL4}: Op386MOVLstoreidx4,
[2]Op{Op386MOVSSstore, Op386LEAL1}: Op386MOVSSstoreidx1,
[2]Op{Op386MOVSSstore, Op386LEAL4}: Op386MOVSSstoreidx4,
[2]Op{Op386MOVSDstore, Op386LEAL1}: Op386MOVSDstoreidx1,
[2]Op{Op386MOVSDstore, Op386LEAL8}: Op386MOVSDstoreidx8,
[2]Op{Op386MOVBstoreconst, Op386LEAL1}: Op386MOVBstoreconstidx1,
[2]Op{Op386MOVWstoreconst, Op386LEAL1}: Op386MOVWstoreconstidx1,
[2]Op{Op386MOVWstoreconst, Op386LEAL2}: Op386MOVWstoreconstidx2,
[2]Op{Op386MOVLstoreconst, Op386LEAL1}: Op386MOVLstoreconstidx1,
[2]Op{Op386MOVLstoreconst, Op386LEAL4}: Op386MOVLstoreconstidx4,
[2]Op{Op386ADDLload, Op386LEAL4}: Op386ADDLloadidx4,
[2]Op{Op386SUBLload, Op386LEAL4}: Op386SUBLloadidx4,
[2]Op{Op386MULLload, Op386LEAL4}: Op386MULLloadidx4,
[2]Op{Op386ANDLload, Op386LEAL4}: Op386ANDLloadidx4,
[2]Op{Op386ORLload, Op386LEAL4}: Op386ORLloadidx4,
[2]Op{Op386XORLload, Op386LEAL4}: Op386XORLloadidx4,
[2]Op{Op386ADDLmodify, Op386LEAL4}: Op386ADDLmodifyidx4,
[2]Op{Op386SUBLmodify, Op386LEAL4}: Op386SUBLmodifyidx4,
[2]Op{Op386ANDLmodify, Op386LEAL4}: Op386ANDLmodifyidx4,
[2]Op{Op386ORLmodify, Op386LEAL4}: Op386ORLmodifyidx4,
[2]Op{Op386XORLmodify, Op386LEAL4}: Op386XORLmodifyidx4,
[2]Op{Op386ADDLconstmodify, Op386LEAL4}: Op386ADDLconstmodifyidx4,
[2]Op{Op386ANDLconstmodify, Op386LEAL4}: Op386ANDLconstmodifyidx4,
[2]Op{Op386ORLconstmodify, Op386LEAL4}: Op386ORLconstmodifyidx4,
[2]Op{Op386XORLconstmodify, Op386LEAL4}: Op386XORLconstmodifyidx4,
}

View File

@ -101,6 +101,9 @@ func (e Edge) Block() *Block {
func (e Edge) Index() int { func (e Edge) Index() int {
return e.i return e.i
} }
func (e Edge) String() string {
return fmt.Sprintf("{%v,%d}", e.b, e.i)
}
// kind controls successors // kind controls successors
// ------------------------------------------ // ------------------------------------------

View File

@ -442,6 +442,7 @@ var passes = [...]pass{
{name: "insert resched checks", fn: insertLoopReschedChecks, {name: "insert resched checks", fn: insertLoopReschedChecks,
disabled: objabi.Preemptibleloops_enabled == 0}, // insert resched checks in loops. disabled: objabi.Preemptibleloops_enabled == 0}, // insert resched checks in loops.
{name: "lower", fn: lower, required: true}, {name: "lower", fn: lower, required: true},
{name: "addressing modes", fn: addressingModes, required: false},
{name: "lowered deadcode for cse", fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again {name: "lowered deadcode for cse", fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again
{name: "lowered cse", fn: cse}, {name: "lowered cse", fn: cse},
{name: "elim unread autos", fn: elimUnreadAutos}, {name: "elim unread autos", fn: elimUnreadAutos},

View File

@ -588,10 +588,6 @@
(MOVWLSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWLSXload <v.Type> [off] {sym} ptr mem) (MOVWLSX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWLSXload <v.Type> [off] {sym} ptr mem)
(MOVWLZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem) (MOVWLZX x:(MOVWload [off] {sym} ptr mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWload <v.Type> [off] {sym} ptr mem)
(MOVBLZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
(MOVWLZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
(MOVWLZX x:(MOVWloadidx2 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
// replace load from same location as preceding store with zero/sign extension (or copy in case of full width) // replace load from same location as preceding store with zero/sign extension (or copy in case of full width)
(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVBLZX x) (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVBLZX x)
(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWLZX x) (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWLZX x)
@ -611,34 +607,22 @@
// fold constants into memory operations // fold constants into memory operations
// Note that this is not always a good idea because if not all the uses of // Note that this is not always a good idea because if not all the uses of
// the ADDQconst get eliminated, we still have to compute the ADDQconst and we now // the ADDLconst get eliminated, we still have to compute the ADDLconst and we now
// have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one. // have potentially two live values (ptr and (ADDLconst [off] ptr)) instead of one.
// Nevertheless, let's do it! // Nevertheless, let's do it!
(MOV(L|W|B|SS|SD)load [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)load [off1+off2] {sym} ptr mem) (MOV(L|W|B|SS|SD)load [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)load [off1+off2] {sym} ptr mem)
(MOV(L|W|B|SS|SD)store [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)store [off1+off2] {sym} ptr val mem) (MOV(L|W|B|SS|SD)store [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(off1+off2) -> (MOV(L|W|B|SS|SD)store [off1+off2] {sym} ptr val mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) -> ((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {sym} val base mem) ((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {sym} val base mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym} val (ADDLconst [off2] base) idx mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {sym} val base idx mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym} val base (ADDLconst [off2] idx) mem) && is32Bit(off1+off2*4) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2*4] {sym} val base idx mem)
((ADD|SUB|MUL|DIV)SSload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) -> ((ADD|SUB|MUL|DIV)SSload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|DIV)SSload [off1+off2] {sym} val base mem) ((ADD|SUB|MUL|DIV)SSload [off1+off2] {sym} val base mem)
((ADD|SUB|MUL|DIV)SDload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) -> ((ADD|SUB|MUL|DIV)SDload [off1] {sym} val (ADDLconst [off2] base) mem) && is32Bit(off1+off2) ->
((ADD|SUB|MUL|DIV)SDload [off1+off2] {sym} val base mem) ((ADD|SUB|MUL|DIV)SDload [off1+off2] {sym} val base mem)
((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym} (ADDLconst [off2] base) val mem) && is32Bit(off1+off2) -> ((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym} (ADDLconst [off2] base) val mem) && is32Bit(off1+off2) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {sym} base val mem) ((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {sym} base val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym} (ADDLconst [off2] base) idx val mem) && is32Bit(off1+off2) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2] {sym} base idx val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym} base (ADDLconst [off2] idx) val mem) && is32Bit(off1+off2*4) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2*4] {sym} base idx val mem)
((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym} (ADDLconst [off2] base) mem) && ValAndOff(valoff1).canAdd(off2) -> ((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym} (ADDLconst [off2] base) mem) && ValAndOff(valoff1).canAdd(off2) ->
((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {sym} base mem) ((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {sym} base mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym} (ADDLconst [off2] base) idx mem) && ValAndOff(valoff1).canAdd(off2) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2)] {sym} base idx mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym} base (ADDLconst [off2] idx) mem) && ValAndOff(valoff1).canAdd(off2*4) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2*4)] {sym} base idx mem)
// Fold constants into stores. // Fold constants into stores.
(MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) -> (MOVLstore [off] {sym} ptr (MOVLconst [c]) mem) && validOff(off) ->
@ -652,7 +636,7 @@
(MOV(L|W|B)storeconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) -> (MOV(L|W|B)storeconst [sc] {s} (ADDLconst [off] ptr) mem) && ValAndOff(sc).canAdd(off) ->
(MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {s} ptr mem) (MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {s} ptr mem)
// We need to fold LEAQ into the MOVx ops so that the live variable analysis knows // We need to fold LEAL into the MOVx ops so that the live variable analysis knows
// what variables are being read/written by the ops. // what variables are being read/written by the ops.
// Note: we turn off this merging for operations on globals when building // Note: we turn off this merging for operations on globals when building
// position-independent code (when Flag_shared is set). // position-independent code (when Flag_shared is set).
@ -672,31 +656,9 @@
&& (ptr.Op != OpSB || !config.ctxt.Flag_shared) -> && (ptr.Op != OpSB || !config.ctxt.Flag_shared) ->
(MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem) (MOV(L|W|B)storeconst [ValAndOff(sc).add(off)] {mergeSym(sym1, sym2)} ptr mem)
// generating indexed loads and stores
(MOV(B|W|L|SS|SD)load [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|SS|SD)loadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWload [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(L|SS)load [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)loadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVSDload [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDloadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L|SS|SD)store [off1] {sym1} (LEAL1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|SS|SD)storeidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVWstore [off1] {sym1} (LEAL2 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(L|SS)store [off1] {sym1} (LEAL4 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)storeidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVSDstore [off1] {sym1} (LEAL8 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVSDstoreidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym1} val (LEAL [off2] {sym2} base) mem) ((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym1} val (LEAL [off2] {sym2} base) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) -> && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {mergeSym(sym1,sym2)} val base mem) ((ADD|SUB|MUL|AND|OR|XOR)Lload [off1+off2] {mergeSym(sym1,sym2)} val base mem)
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1] {sym1} val (LEAL [off2] {sym2} base) idx mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {mergeSym(sym1,sym2)} val base idx mem)
((ADD|SUB|MUL|DIV)SSload [off1] {sym1} val (LEAL [off2] {sym2} base) mem) ((ADD|SUB|MUL|DIV)SSload [off1] {sym1} val (LEAL [off2] {sym2} base) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) -> && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|MUL|DIV)SSload [off1+off2] {mergeSym(sym1,sym2)} val base mem) ((ADD|SUB|MUL|DIV)SSload [off1+off2] {mergeSym(sym1,sym2)} val base mem)
@ -706,97 +668,20 @@
((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym1} (LEAL [off2] {sym2} base) val mem) ((ADD|SUB|AND|OR|XOR)Lmodify [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) -> && is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem) ((ADD|SUB|AND|OR|XOR)Lmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem)
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1] {sym1} (LEAL [off2] {sym2} base) idx val mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off1+off2] {mergeSym(sym1,sym2)} base idx val mem)
((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym1} (LEAL [off2] {sym2} base) mem) ((ADD|AND|OR|XOR)Lconstmodify [valoff1] {sym1} (LEAL [off2] {sym2} base) mem)
&& ValAndOff(valoff1).canAdd(off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) -> && ValAndOff(valoff1).canAdd(off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {mergeSym(sym1,sym2)} base mem) ((ADD|AND|OR|XOR)Lconstmodify [ValAndOff(valoff1).add(off2)] {mergeSym(sym1,sym2)} base mem)
((ADD|AND|OR|XOR)Lconstmodifyidx4 [valoff1] {sym1} (LEAL [off2] {sym2} base) idx mem)
&& ValAndOff(valoff1).canAdd(off2) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_shared) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [ValAndOff(valoff1).add(off2)] {mergeSym(sym1,sym2)} base idx mem)
(MOV(B|W|L|SS|SD)load [off] {sym} (ADDL ptr idx) mem) && ptr.Op != OpSB -> (MOV(B|W|L|SS|SD)loadidx1 [off] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)store [off] {sym} (ADDL ptr idx) val mem) && ptr.Op != OpSB -> (MOV(B|W|L|SS|SD)storeidx1 [off] {sym} ptr idx val mem)
(MOV(B|W|L)storeconst [x] {sym1} (LEAL1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWstoreconst [x] {sym1} (LEAL2 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLstoreconst [x] {sym1} (LEAL4 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L)storeconst [x] {sym} (ADDL ptr idx) mem) -> (MOV(B|W|L)storeconstidx1 [x] {sym} ptr idx mem)
// combine SHLL into indexed loads and stores
(MOVWloadidx1 [c] {sym} ptr (SHLLconst [1] idx) mem) -> (MOVWloadidx2 [c] {sym} ptr idx mem)
(MOVLloadidx1 [c] {sym} ptr (SHLLconst [2] idx) mem) -> (MOVLloadidx4 [c] {sym} ptr idx mem)
(MOVWstoreidx1 [c] {sym} ptr (SHLLconst [1] idx) val mem) -> (MOVWstoreidx2 [c] {sym} ptr idx val mem)
(MOVLstoreidx1 [c] {sym} ptr (SHLLconst [2] idx) val mem) -> (MOVLstoreidx4 [c] {sym} ptr idx val mem)
(MOVWstoreconstidx1 [c] {sym} ptr (SHLLconst [1] idx) mem) -> (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
(MOVLstoreconstidx1 [c] {sym} ptr (SHLLconst [2] idx) mem) -> (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
// combine ADDL into indexed loads and stores
(MOV(B|W|L|SS|SD)loadidx1 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOV(B|W|L|SS|SD)loadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVWloadidx2 [int64(int32(c+d))] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOV(L|SS)loadidx4 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVSDloadidx8 [c] {sym} (ADDLconst [d] ptr) idx mem) -> (MOVSDloadidx8 [int64(int32(c+d))] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)storeidx1 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOV(B|W|L|SS|SD)storeidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVWstoreidx2 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOV(L|SS)storeidx4 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVSDstoreidx8 [c] {sym} (ADDLconst [d] ptr) idx val mem) -> (MOVSDstoreidx8 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOV(B|W|L|SS|SD)loadidx1 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOV(B|W|L|SS|SD)loadidx1 [int64(int32(c+d))] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVWloadidx2 [int64(int32(c+2*d))] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOV(L|SS)loadidx4 [int64(int32(c+4*d))] {sym} ptr idx mem)
(MOVSDloadidx8 [c] {sym} ptr (ADDLconst [d] idx) mem) -> (MOVSDloadidx8 [int64(int32(c+8*d))] {sym} ptr idx mem)
(MOV(B|W|L|SS|SD)storeidx1 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOV(B|W|L|SS|SD)storeidx1 [int64(int32(c+d))] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVWstoreidx2 [int64(int32(c+2*d))] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOV(L|SS)storeidx4 [int64(int32(c+4*d))] {sym} ptr idx val mem)
(MOVSDstoreidx8 [c] {sym} ptr (ADDLconst [d] idx) val mem) -> (MOVSDstoreidx8 [int64(int32(c+8*d))] {sym} ptr idx val mem)
// Merge load/store to op // Merge load/store to op
((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) -> ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem) ((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) -> ((ADD|AND|OR|XOR|SUB|MUL)Lload x [off] {sym} ptr mem)
((ADD|AND|OR|XOR|SUB|MUL)L x l:(MOVLloadidx4 [off] {sym} ptr idx mem)) && canMergeLoadClobber(v, l, x) && clobber(l) ->
((ADD|AND|OR|XOR|SUB|MUL)Lloadidx4 x [off] {sym} ptr idx mem)
((ADD|SUB|MUL|AND|OR|XOR)Lload [off1] {sym1} val (LEAL4 [off2] {sym2} ptr idx) mem)
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
((ADD|SUB|MUL|AND|OR|XOR)Lloadidx4 [off1+off2] {mergeSym(sym1,sym2)} val ptr idx mem)
((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem) ((ADD|SUB|MUL|DIV)SD x l:(MOVSDload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SDload x [off] {sym} ptr mem)
((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem) ((ADD|SUB|MUL|DIV)SS x l:(MOVSSload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && !config.use387 && clobber(l) -> ((ADD|SUB|MUL|DIV)SSload x [off] {sym} ptr mem)
(MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) -> ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) (MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lload x [off] {sym} ptr mem) mem) && y.Uses==1 && clobber(y) -> ((ADD|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
(MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) -> (MOVLstore {sym} [off] ptr y:((ADD|SUB|AND|OR|XOR)L l:(MOVLload [off] {sym} ptr mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) ->
((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem) ((ADD|SUB|AND|OR|XOR)Lmodify [off] {sym} ptr x mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|AND|OR|XOR)Lloadidx4 x [off] {sym} ptr idx mem) mem) && y.Uses==1 && clobber(y) ->
((ADD|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx x mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|SUB|AND|OR|XOR)L l:(MOVLloadidx4 [off] {sym} ptr idx mem) x) mem) && y.Uses==1 && l.Uses==1 && clobber(y, l) ->
((ADD|SUB|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx x mem)
(MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lconst [c] l:(MOVLload [off] {sym} ptr mem)) mem) (MOVLstore {sym} [off] ptr y:((ADD|AND|OR|XOR)Lconst [c] l:(MOVLload [off] {sym} ptr mem)) mem)
&& y.Uses==1 && l.Uses==1 && clobber(y, l) && validValAndOff(c,off) -> && y.Uses==1 && l.Uses==1 && clobber(y, l) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodify [makeValAndOff(c,off)] {sym} ptr mem) ((ADD|AND|OR|XOR)Lconstmodify [makeValAndOff(c,off)] {sym} ptr mem)
(MOVLstoreidx4 {sym} [off] ptr idx y:((ADD|AND|OR|XOR)Lconst [c] l:(MOVLloadidx4 [off] {sym} ptr idx mem)) mem)
&& y.Uses==1 && l.Uses==1 && clobber(y, l) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [makeValAndOff(c,off)] {sym} ptr idx mem)
((ADD|AND|OR|XOR)Lmodifyidx4 [off] {sym} ptr idx (MOVLconst [c]) mem) && validValAndOff(c,off) ->
((ADD|AND|OR|XOR)Lconstmodifyidx4 [makeValAndOff(c,off)] {sym} ptr idx mem)
(SUBLmodifyidx4 [off] {sym} ptr idx (MOVLconst [c]) mem) && validValAndOff(-c,off) ->
(ADDLconstmodifyidx4 [makeValAndOff(-c,off)] {sym} ptr idx mem)
(MOV(B|W|L)storeconstidx1 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} (ADDLconst [c] ptr) idx mem) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOV(B|W|L)storeconstidx1 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOV(B|W|L)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} ptr (ADDLconst [c] idx) mem) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
// fold LEALs together // fold LEALs together
(LEAL [off1] {sym1} (LEAL [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (LEAL [off1] {sym1} (LEAL [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
@ -826,6 +711,16 @@
(LEAL [off1] {sym1} (LEAL8 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (LEAL [off1] {sym1} (LEAL8 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y) (LEAL8 [off1+off2] {mergeSym(sym1,sym2)} x y)
// LEAL[1248] into LEAL[1248]. Only some such merges are possible.
(LEAL1 [off1] {sym1} x (LEAL1 [off2] {sym2} y y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAL2 [off1+off2] {mergeSym(sym1, sym2)} x y)
(LEAL1 [off1] {sym1} x (LEAL1 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAL2 [off1+off2] {mergeSym(sym1, sym2)} y x)
(LEAL2 [off1] {sym1} x (LEAL1 [off2] {sym2} y y)) && is32Bit(off1+2*off2) && sym2 == nil ->
(LEAL4 [off1+2*off2] {sym1} x y)
(LEAL4 [off1] {sym1} x (LEAL1 [off2] {sym2} y y)) && is32Bit(off1+4*off2) && sym2 == nil ->
(LEAL8 [off1+4*off2] {sym1} x y)
// Absorb InvertFlags into branches. // Absorb InvertFlags into branches.
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no) (LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
(GT (InvertFlags cmp) yes no) -> (LT cmp yes no) (GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
@ -1039,23 +934,27 @@
// TEST %reg,%reg is shorter than CMP // TEST %reg,%reg is shorter than CMP
(CMP(L|W|B)const x [0]) -> (TEST(L|W|B) x x) (CMP(L|W|B)const x [0]) -> (TEST(L|W|B) x x)
// Convert LEAL1 back to ADDL if we can
(LEAL1 [0] x y) && v.Aux == nil -> (ADDL x y)
// Combining byte loads into larger (unaligned) loads. // Combining byte loads into larger (unaligned) loads.
// There are many ways these combinations could occur. This is // There are many ways these combinations could occur. This is
// designed to match the way encoding/binary.LittleEndian does it. // designed to match the way encoding/binary.LittleEndian does it.
(ORL x0:(MOVBload [i0] {s} p mem) (ORL x0:(MOVBload [i0] {s} p0 mem)
s0:(SHLLconst [8] x1:(MOVBload [i1] {s} p mem))) s0:(SHLLconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1 && i1 == i0+1
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& s0.Uses == 1 && s0.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, s0) && clobber(x0, x1, s0)
-> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p0 mem)
(ORL o0:(ORL (ORL o0:(ORL
x0:(MOVWload [i0] {s} p mem) x0:(MOVWload [i0] {s} p0 mem)
s0:(SHLLconst [16] x1:(MOVBload [i2] {s} p mem))) s0:(SHLLconst [16] x1:(MOVBload [i2] {s} p1 mem)))
s1:(SHLLconst [24] x2:(MOVBload [i3] {s} p mem))) s1:(SHLLconst [24] x2:(MOVBload [i3] {s} p2 mem)))
&& i2 == i0+2 && i2 == i0+2
&& i3 == i0+3 && i3 == i0+3
&& x0.Uses == 1 && x0.Uses == 1
@ -1064,126 +963,84 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& o0.Uses == 1 && o0.Uses == 1
&& same(p0, p1, 1)
&& same(p1, p2, 1)
&& mergePoint(b,x0,x1,x2) != nil && mergePoint(b,x0,x1,x2) != nil
&& clobber(x0, x1, x2, s0, s1, o0) && clobber(x0, x1, x2, s0, s1, o0)
-> @mergePoint(b,x0,x1,x2) (MOVLload [i0] {s} p mem) -> @mergePoint(b,x0,x1,x2) (MOVLload [i0] {s} p0 mem)
(ORL x0:(MOVBloadidx1 [i0] {s} p idx mem)
s0:(SHLLconst [8] x1:(MOVBloadidx1 [i1] {s} p idx mem)))
&& i1==i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, s0)
-> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i0] {s} p idx mem)
(ORL o0:(ORL
x0:(MOVWloadidx1 [i0] {s} p idx mem)
s0:(SHLLconst [16] x1:(MOVBloadidx1 [i2] {s} p idx mem)))
s1:(SHLLconst [24] x2:(MOVBloadidx1 [i3] {s} p idx mem)))
&& i2 == i0+2
&& i3 == i0+3
&& x0.Uses == 1
&& x1.Uses == 1
&& x2.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& o0.Uses == 1
&& mergePoint(b,x0,x1,x2) != nil
&& clobber(x0, x1, x2, s0, s1, o0)
-> @mergePoint(b,x0,x1,x2) (MOVLloadidx1 <v.Type> [i0] {s} p idx mem)
// Combine constant stores into larger (unaligned) stores. // Combine constant stores into larger (unaligned) stores.
(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem)) (MOVBstoreconst [c] {s} p1 x:(MOVBstoreconst [a] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem) -> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem)) (MOVBstoreconst [a] {s} p1 x:(MOVBstoreconst [c] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem) -> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem)) (MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem) -> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem)) (MOVWstoreconst [a] {s} p1 x:(MOVWstoreconst [c] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem) -> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
(MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
(MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLLconst <i.Type> [1] i) mem)
// Combine stores into larger (unaligned) stores. // Combine stores into larger (unaligned) stores.
(MOVBstore [i] {s} p (SHR(W|L)const [8] w) x:(MOVBstore [i-1] {s} p w mem)) (MOVBstore [i] {s} p1 (SHR(W|L)const [8] w) x:(MOVBstore [i-1] {s} p0 w mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i-1] {s} p w mem) -> (MOVWstore [i-1] {s} p0 w mem)
(MOVBstore [i] {s} p w x:(MOVBstore {s} [i+1] p (SHR(W|L)const [8] w) mem)) (MOVBstore [i] {s} p1 w x:(MOVBstore {s} [i+1] p0 (SHR(W|L)const [8] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i] {s} p w mem) -> (MOVWstore [i] {s} p0 w mem)
(MOVBstore [i] {s} p (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRLconst [j-8] w) mem)) (MOVBstore [i] {s} p1 (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p0 w0:(SHRLconst [j-8] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i-1] {s} p w0 mem) -> (MOVWstore [i-1] {s} p0 w0 mem)
(MOVWstore [i] {s} p (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p w mem)) (MOVWstore [i] {s} p1 (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p0 w mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstore [i-2] {s} p w mem) -> (MOVLstore [i-2] {s} p0 w mem)
(MOVWstore [i] {s} p (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRLconst [j-16] w) mem)) (MOVWstore [i] {s} p1 (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p0 w0:(SHRLconst [j-16] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstore [i-2] {s} p w0 mem) -> (MOVLstore [i-2] {s} p0 w0 mem)
(MOVBstoreidx1 [i] {s} p idx (SHR(L|W)const [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem)) // Move constant offsets from LEALx up into load. This lets the above combining
&& x.Uses == 1 // rules discover indexed load-combining instances.
&& clobber(x) (MOV(B|W|L)load [i0] {s0} l:(LEAL1 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOVWstoreidx1 [i-1] {s} p idx w mem) -> (MOV(B|W|L)load [i0+i1] {s0} (LEAL1 <l.Type> [0] {s1} x y) mem)
(MOVBstoreidx1 [i] {s} p idx w x:(MOVBstoreidx1 [i+1] {s} p idx (SHR(L|W)const [8] w) mem)) (MOV(B|W|L)load [i0] {s0} l:(LEAL2 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
&& x.Uses == 1 -> (MOV(B|W|L)load [i0+i1] {s0} (LEAL2 <l.Type> [0] {s1} x y) mem)
&& clobber(x) (MOV(B|W|L)load [i0] {s0} l:(LEAL4 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOVWstoreidx1 [i] {s} p idx w mem) -> (MOV(B|W|L)load [i0+i1] {s0} (LEAL4 <l.Type> [0] {s1} x y) mem)
(MOVBstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRLconst [j-8] w) mem)) (MOV(B|W|L)load [i0] {s0} l:(LEAL8 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
&& x.Uses == 1 -> (MOV(B|W|L)load [i0+i1] {s0} (LEAL8 <l.Type> [0] {s1} x y) mem)
&& clobber(x)
-> (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
(MOVWstoreidx1 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p idx w mem)
(MOVWstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
(MOVWstoreidx2 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem)) (MOV(B|W|L)store [i0] {s0} l:(LEAL1 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
&& x.Uses == 1 -> (MOV(B|W|L)store [i0+i1] {s0} (LEAL1 <l.Type> [0] {s1} x y) val mem)
&& clobber(x) (MOV(B|W|L)store [i0] {s0} l:(LEAL2 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w mem) -> (MOV(B|W|L)store [i0+i1] {s0} (LEAL2 <l.Type> [0] {s1} x y) val mem)
(MOVWstoreidx2 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem)) (MOV(B|W|L)store [i0] {s0} l:(LEAL4 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
&& x.Uses == 1 -> (MOV(B|W|L)store [i0+i1] {s0} (LEAL4 <l.Type> [0] {s1} x y) val mem)
&& clobber(x) (MOV(B|W|L)store [i0] {s0} l:(LEAL8 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w0 mem) -> (MOV(B|W|L)store [i0+i1] {s0} (LEAL8 <l.Type> [0] {s1} x y) val mem)
// For PIC, break floating-point constant loading into two instructions so we have // For PIC, break floating-point constant loading into two instructions so we have
// a register to use for holding the address of the constant pool entry. // a register to use for holding the address of the constant pool entry.

View File

@ -1043,12 +1043,6 @@
(MOVWQZX x) && zeroUpper48Bits(x,3) -> x (MOVWQZX x) && zeroUpper48Bits(x,3) -> x
(MOVBQZX x) && zeroUpper56Bits(x,3) -> x (MOVBQZX x) && zeroUpper56Bits(x,3) -> x
(MOVBQZX x:(MOVBloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVBloadidx1 <v.Type> [off] {sym} ptr idx mem)
(MOVWQZX x:(MOVWloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx1 <v.Type> [off] {sym} ptr idx mem)
(MOVWQZX x:(MOVWloadidx2 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVWloadidx2 <v.Type> [off] {sym} ptr idx mem)
(MOVLQZX x:(MOVLloadidx1 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLloadidx1 <v.Type> [off] {sym} ptr idx mem)
(MOVLQZX x:(MOVLloadidx4 [off] {sym} ptr idx mem)) && x.Uses == 1 && clobber(x) -> @x.Block (MOVLloadidx4 <v.Type> [off] {sym} ptr idx mem)
// replace load from same location as preceding store with zero/sign extension (or copy in case of full width) // replace load from same location as preceding store with zero/sign extension (or copy in case of full width)
(MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVBQZX x) (MOVBload [off] {sym} ptr (MOVBstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVBQZX x)
(MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWQZX x) (MOVWload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) && sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) -> (MOVWQZX x)
@ -1166,86 +1160,6 @@
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
((ADD|SUB|AND|OR|XOR|BTC|BTR|BTS)Lmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem) ((ADD|SUB|AND|OR|XOR|BTC|BTR|BTS)Lmodify [off1+off2] {mergeSym(sym1,sym2)} base val mem)
// generating indexed loads and stores
(MOV(B|W|L|Q|SS|SD)load [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|Q|SS|SD)loadidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWload [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWloadidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(L|SS)load [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)loadidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(L|Q|SD)load [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|Q|SD)loadidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L|Q|SS|SD)store [off1] {sym1} (LEAQ1 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|Q|SS|SD)storeidx1 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOVWstore [off1] {sym1} (LEAQ2 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOVWstoreidx2 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(L|SS)store [off1] {sym1} (LEAQ4 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|SS)storeidx4 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(L|Q|SD)store [off1] {sym1} (LEAQ8 [off2] {sym2} ptr idx) val mem) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(MOV(L|Q|SD)storeidx8 [off1+off2] {mergeSym(sym1,sym2)} ptr idx val mem)
(MOV(B|W|L|Q|SS|SD)load [off] {sym} (ADDQ ptr idx) mem) && ptr.Op != OpSB ->
(MOV(B|W|L|Q|SS|SD)loadidx1 [off] {sym} ptr idx mem)
(MOV(B|W|L|Q|SS|SD)store [off] {sym} (ADDQ ptr idx) val mem) && ptr.Op != OpSB ->
(MOV(B|W|L|Q|SS|SD)storeidx1 [off] {sym} ptr idx val mem)
(MOV(B|W|L|Q)storeconst [x] {sym1} (LEAQ1 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOV(B|W|L|Q)storeconstidx1 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVWstoreconst [x] {sym1} (LEAQ2 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVWstoreconstidx2 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVLstoreconst [x] {sym1} (LEAQ4 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVLstoreconstidx4 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOVQstoreconst [x] {sym1} (LEAQ8 [off] {sym2} ptr idx) mem) && canMergeSym(sym1, sym2) ->
(MOVQstoreconstidx8 [ValAndOff(x).add(off)] {mergeSym(sym1,sym2)} ptr idx mem)
(MOV(B|W|L|Q)storeconst [x] {sym} (ADDQ ptr idx) mem) -> (MOV(B|W|L|Q)storeconstidx1 [x] {sym} ptr idx mem)
// combine SHLQ into indexed loads and stores
(MOVWloadidx1 [c] {sym} ptr (SHLQconst [1] idx) mem) -> (MOVWloadidx2 [c] {sym} ptr idx mem)
(MOV(L|SS)loadidx1 [c] {sym} ptr (SHLQconst [2] idx) mem) -> (MOV(L|SS)loadidx4 [c] {sym} ptr idx mem)
(MOV(L|Q|SD)loadidx1 [c] {sym} ptr (SHLQconst [3] idx) mem) -> (MOV(L|Q|SD)loadidx8 [c] {sym} ptr idx mem)
(MOVWstoreidx1 [c] {sym} ptr (SHLQconst [1] idx) val mem) -> (MOVWstoreidx2 [c] {sym} ptr idx val mem)
(MOV(L|SS)storeidx1 [c] {sym} ptr (SHLQconst [2] idx) val mem) -> (MOV(L|SS)storeidx4 [c] {sym} ptr idx val mem)
(MOV(L|Q|SD)storeidx1 [c] {sym} ptr (SHLQconst [3] idx) val mem) -> (MOV(L|Q|SD)storeidx8 [c] {sym} ptr idx val mem)
(MOVWstoreconstidx1 [c] {sym} ptr (SHLQconst [1] idx) mem) -> (MOVWstoreconstidx2 [c] {sym} ptr idx mem)
(MOVLstoreconstidx1 [c] {sym} ptr (SHLQconst [2] idx) mem) -> (MOVLstoreconstidx4 [c] {sym} ptr idx mem)
(MOVQstoreconstidx1 [c] {sym} ptr (SHLQconst [3] idx) mem) -> (MOVQstoreconstidx8 [c] {sym} ptr idx mem)
// combine ADDQ into pointer of indexed loads and stores
(MOV(B|W|L|Q|SS|SD)loadidx1 [c] {sym} (ADDQconst [d] ptr) idx mem) && is32Bit(c+d) -> (MOV(B|W|L|Q|SS|SD)loadidx1 [c+d] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} (ADDQconst [d] ptr) idx mem) && is32Bit(c+d) -> (MOVWloadidx2 [c+d] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} (ADDQconst [d] ptr) idx mem) && is32Bit(c+d) -> (MOV(L|SS)loadidx4 [c+d] {sym} ptr idx mem)
(MOV(L|Q|SD)loadidx8 [c] {sym} (ADDQconst [d] ptr) idx mem) && is32Bit(c+d) -> (MOV(L|Q|SD)loadidx8 [c+d] {sym} ptr idx mem)
(MOV(B|W|L|Q|SS|SD)storeidx1 [c] {sym} (ADDQconst [d] ptr) idx val mem) && is32Bit(c+d) -> (MOV(B|W|L|Q|SS|SD)storeidx1 [c+d] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} (ADDQconst [d] ptr) idx val mem) && is32Bit(c+d) -> (MOVWstoreidx2 [c+d] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} (ADDQconst [d] ptr) idx val mem) && is32Bit(c+d) -> (MOV(L|SS)storeidx4 [c+d] {sym} ptr idx val mem)
(MOV(L|Q|SD)storeidx8 [c] {sym} (ADDQconst [d] ptr) idx val mem) && is32Bit(c+d) -> (MOV(L|Q|SD)storeidx8 [c+d] {sym} ptr idx val mem)
// combine ADDQ into index of indexed loads and stores
(MOV(B|W|L|Q|SS|SD)loadidx1 [c] {sym} ptr (ADDQconst [d] idx) mem) && is32Bit(c+d) -> (MOV(B|W|L|Q|SS|SD)loadidx1 [c+d] {sym} ptr idx mem)
(MOVWloadidx2 [c] {sym} ptr (ADDQconst [d] idx) mem) && is32Bit(c+2*d) -> (MOVWloadidx2 [c+2*d] {sym} ptr idx mem)
(MOV(L|SS)loadidx4 [c] {sym} ptr (ADDQconst [d] idx) mem) && is32Bit(c+4*d) -> (MOV(L|SS)loadidx4 [c+4*d] {sym} ptr idx mem)
(MOV(L|Q|SD)loadidx8 [c] {sym} ptr (ADDQconst [d] idx) mem) && is32Bit(c+8*d) -> (MOV(L|Q|SD)loadidx8 [c+8*d] {sym} ptr idx mem)
(MOV(B|W|L|Q|SS|SD)storeidx1 [c] {sym} ptr (ADDQconst [d] idx) val mem) && is32Bit(c+d) -> (MOV(B|W|L|Q|SS|SD)storeidx1 [c+d] {sym} ptr idx val mem)
(MOVWstoreidx2 [c] {sym} ptr (ADDQconst [d] idx) val mem) && is32Bit(c+2*d) -> (MOVWstoreidx2 [c+2*d] {sym} ptr idx val mem)
(MOV(L|SS)storeidx4 [c] {sym} ptr (ADDQconst [d] idx) val mem) && is32Bit(c+4*d) -> (MOV(L|SS)storeidx4 [c+4*d] {sym} ptr idx val mem)
(MOV(L|Q|SD)storeidx8 [c] {sym} ptr (ADDQconst [d] idx) val mem) && is32Bit(c+8*d) -> (MOV(L|Q|SD)storeidx8 [c+8*d] {sym} ptr idx val mem)
(MOV(B|W|L|Q)storeconstidx1 [x] {sym} (ADDQconst [c] ptr) idx mem) && ValAndOff(x).canAdd(c) -> (MOV(B|W|L|Q)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} (ADDQconst [c] ptr) idx mem) && ValAndOff(x).canAdd(c) -> (MOVWstoreconstidx2 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} (ADDQconst [c] ptr) idx mem) && ValAndOff(x).canAdd(c) -> (MOVLstoreconstidx4 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVQstoreconstidx8 [x] {sym} (ADDQconst [c] ptr) idx mem) && ValAndOff(x).canAdd(c) -> (MOVQstoreconstidx8 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOV(B|W|L|Q)storeconstidx1 [x] {sym} ptr (ADDQconst [c] idx) mem) && ValAndOff(x).canAdd(c) -> (MOV(B|W|L|Q)storeconstidx1 [ValAndOff(x).add(c)] {sym} ptr idx mem)
(MOVWstoreconstidx2 [x] {sym} ptr (ADDQconst [c] idx) mem) && ValAndOff(x).canAdd(2*c) -> (MOVWstoreconstidx2 [ValAndOff(x).add(2*c)] {sym} ptr idx mem)
(MOVLstoreconstidx4 [x] {sym} ptr (ADDQconst [c] idx) mem) && ValAndOff(x).canAdd(4*c) -> (MOVLstoreconstidx4 [ValAndOff(x).add(4*c)] {sym} ptr idx mem)
(MOVQstoreconstidx8 [x] {sym} ptr (ADDQconst [c] idx) mem) && ValAndOff(x).canAdd(8*c) -> (MOVQstoreconstidx8 [ValAndOff(x).add(8*c)] {sym} ptr idx mem)
// fold LEAQs together // fold LEAQs together
(LEAQ [off1] {sym1} (LEAQ [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (LEAQ [off1] {sym1} (LEAQ [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAQ [off1+off2] {mergeSym(sym1,sym2)} x) (LEAQ [off1+off2] {mergeSym(sym1,sym2)} x)
@ -1274,6 +1188,17 @@
(LEAQ [off1] {sym1} (LEAQ8 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) -> (LEAQ [off1] {sym1} (LEAQ8 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y) (LEAQ8 [off1+off2] {mergeSym(sym1,sym2)} x y)
// LEAQ[1248] into LEAQ[1248]. Only some such merges are possible.
(LEAQ1 [off1] {sym1} x (LEAQ1 [off2] {sym2} y y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAQ2 [off1+off2] {mergeSym(sym1, sym2)} x y)
(LEAQ1 [off1] {sym1} x (LEAQ1 [off2] {sym2} x y)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(LEAQ2 [off1+off2] {mergeSym(sym1, sym2)} y x)
(LEAQ2 [off1] {sym1} x (LEAQ1 [off2] {sym2} y y)) && is32Bit(off1+2*off2) && sym2 == nil ->
(LEAQ4 [off1+2*off2] {sym1} x y)
(LEAQ4 [off1] {sym1} x (LEAQ1 [off2] {sym2} y y)) && is32Bit(off1+4*off2) && sym2 == nil ->
(LEAQ8 [off1+4*off2] {sym1} x y)
// TODO: more?
// Absorb InvertFlags into branches. // Absorb InvertFlags into branches.
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no) (LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
(GT (InvertFlags cmp) yes no) -> (LT cmp yes no) (GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
@ -1329,7 +1254,10 @@
(CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT) (CMPWconst (ANDLconst _ [m]) [n]) && 0 <= int16(m) && int16(m) < int16(n) -> (FlagLT_ULT)
(CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < int8(n) -> (FlagLT_ULT) (CMPBconst (ANDLconst _ [m]) [n]) && 0 <= int8(m) && int8(m) < int8(n) -> (FlagLT_ULT)
(TEST(Q|L)const [c] (MOV(Q|L)const [c])) -> (FlagEQ) // TESTQ c c sets flags like CMPQ c 0.
(TEST(Q|L)const [c] (MOV(Q|L)const [c])) && c == 0 -> (FlagEQ)
(TEST(Q|L)const [c] (MOV(Q|L)const [c])) && c < 0 -> (FlagLT_UGT)
(TEST(Q|L)const [c] (MOV(Q|L)const [c])) && c > 0 -> (FlagGT_UGT)
// TODO: DIVxU also. // TODO: DIVxU also.
@ -1552,60 +1480,65 @@
// Little-endian loads // Little-endian loads
(ORL x0:(MOVBload [i0] {s} p mem) (ORL x0:(MOVBload [i0] {s} p0 mem)
sh:(SHLLconst [8] x1:(MOVBload [i1] {s} p mem))) sh:(SHLLconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1 && i1 == i0+1
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p0 mem)
(ORQ x0:(MOVBload [i0] {s} p mem) (ORQ x0:(MOVBload [i0] {s} p0 mem)
sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p mem))) sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1 && i1 == i0+1
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVWload [i0] {s} p0 mem)
(ORL x0:(MOVWload [i0] {s} p mem) (ORL x0:(MOVWload [i0] {s} p0 mem)
sh:(SHLLconst [16] x1:(MOVWload [i1] {s} p mem))) sh:(SHLLconst [16] x1:(MOVWload [i1] {s} p1 mem)))
&& i1 == i0+2 && i1 == i0+2
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVLload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVLload [i0] {s} p0 mem)
(ORQ x0:(MOVWload [i0] {s} p mem) (ORQ x0:(MOVWload [i0] {s} p0 mem)
sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p mem))) sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p1 mem)))
&& i1 == i0+2 && i1 == i0+2
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVLload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVLload [i0] {s} p0 mem)
(ORQ x0:(MOVLload [i0] {s} p mem) (ORQ x0:(MOVLload [i0] {s} p0 mem)
sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p mem))) sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p1 mem)))
&& i1 == i0+4 && i1 == i0+4
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVQload [i0] {s} p mem) -> @mergePoint(b,x0,x1) (MOVQload [i0] {s} p0 mem)
(ORL (ORL
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem)) s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p0 mem))
or:(ORL or:(ORL
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem)) s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p1 mem))
y)) y))
&& i1 == i0+1 && i1 == i0+1
&& j1 == j0+8 && j1 == j0+8
@ -1615,14 +1548,15 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or) && clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j0] (MOVWload [i0] {s} p mem)) y) -> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j0] (MOVWload [i0] {s} p0 mem)) y)
(ORQ (ORQ
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p0 mem))
or:(ORQ or:(ORQ
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p1 mem))
y)) y))
&& i1 == i0+1 && i1 == i0+1
&& j1 == j0+8 && j1 == j0+8
@ -1632,14 +1566,15 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or) && clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVWload [i0] {s} p mem)) y) -> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVWload [i0] {s} p0 mem)) y)
(ORQ (ORQ
s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p mem)) s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p0 mem))
or:(ORQ or:(ORQ
s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p mem)) s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p1 mem))
y)) y))
&& i1 == i0+2 && i1 == i0+2
&& j1 == j0+16 && j1 == j0+16
@ -1649,180 +1584,105 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or) && clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVLload [i0] {s} p mem)) y) -> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVLload [i0] {s} p0 mem)) y)
// Little-endian indexed loads // Little-endian indexed loads
(ORL x0:(MOVBloadidx1 [i0] {s} p idx mem) // Move constants offsets from LEAQx up into load. This lets the above combining
sh:(SHLLconst [8] x1:(MOVBloadidx1 [i1] {s} p idx mem))) // rules discover indexed load-combining instances.
&& i1 == i0+1 (MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ1 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
&& x0.Uses == 1 -> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ1 <l.Type> [0] {s1} x y) mem)
&& x1.Uses == 1 (MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ2 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
&& sh.Uses == 1 -> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ2 <l.Type> [0] {s1} x y) mem)
&& mergePoint(b,x0,x1) != nil (MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ4 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
&& clobber(x0, x1, sh) -> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ4 <l.Type> [0] {s1} x y) mem)
-> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i0] {s} p idx mem) (MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ8 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ8 <l.Type> [0] {s1} x y) mem)
(ORQ x0:(MOVBloadidx1 [i0] {s} p idx mem) (MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ1 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
sh:(SHLQconst [8] x1:(MOVBloadidx1 [i1] {s} p idx mem))) -> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ1 <l.Type> [0] {s1} x y) val mem)
&& i1 == i0+1 (MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ2 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
&& x0.Uses == 1 -> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ2 <l.Type> [0] {s1} x y) val mem)
&& x1.Uses == 1 (MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ4 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
&& sh.Uses == 1 -> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ4 <l.Type> [0] {s1} x y) val mem)
&& mergePoint(b,x0,x1) != nil (MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ8 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
&& clobber(x0, x1, sh) -> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ8 <l.Type> [0] {s1} x y) val mem)
-> @mergePoint(b,x0,x1) (MOVWloadidx1 <v.Type> [i0] {s} p idx mem)
(ORL x0:(MOVWloadidx1 [i0] {s} p idx mem)
sh:(SHLLconst [16] x1:(MOVWloadidx1 [i1] {s} p idx mem)))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVLloadidx1 [i0] {s} p idx mem)
(ORQ x0:(MOVWloadidx1 [i0] {s} p idx mem)
sh:(SHLQconst [16] x1:(MOVWloadidx1 [i1] {s} p idx mem)))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVLloadidx1 [i0] {s} p idx mem)
(ORQ x0:(MOVLloadidx1 [i0] {s} p idx mem)
sh:(SHLQconst [32] x1:(MOVLloadidx1 [i1] {s} p idx mem)))
&& i1 == i0+4
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVQloadidx1 [i0] {s} p idx mem)
(ORL
s1:(SHLLconst [j1] x1:(MOVBloadidx1 [i1] {s} p idx mem))
or:(ORL
s0:(SHLLconst [j0] x0:(MOVBloadidx1 [i0] {s} p idx mem))
y))
&& i1 == i0+1
&& j1 == j0+8
&& j0 % 16 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j0] (MOVWloadidx1 [i0] {s} p idx mem)) y)
(ORQ
s1:(SHLQconst [j1] x1:(MOVBloadidx1 [i1] {s} p idx mem))
or:(ORQ
s0:(SHLQconst [j0] x0:(MOVBloadidx1 [i0] {s} p idx mem))
y))
&& i1 == i0+1
&& j1 == j0+8
&& j0 % 16 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVWloadidx1 [i0] {s} p idx mem)) y)
(ORQ
s1:(SHLQconst [j1] x1:(MOVWloadidx1 [i1] {s} p idx mem))
or:(ORQ
s0:(SHLQconst [j0] x0:(MOVWloadidx1 [i0] {s} p idx mem))
y))
&& i1 == i0+2
&& j1 == j0+16
&& j0 % 32 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j0] (MOVLloadidx1 [i0] {s} p idx mem)) y)
// Big-endian loads // Big-endian loads
(ORL (ORL
x1:(MOVBload [i1] {s} p mem) x1:(MOVBload [i1] {s} p0 mem)
sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p mem))) sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p1 mem)))
&& i1 == i0+1 && i1 == i0+1
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWload [i0] {s} p mem)) -> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWload [i0] {s} p0 mem))
(ORQ (ORQ
x1:(MOVBload [i1] {s} p mem) x1:(MOVBload [i1] {s} p0 mem)
sh:(SHLQconst [8] x0:(MOVBload [i0] {s} p mem))) sh:(SHLQconst [8] x0:(MOVBload [i0] {s} p1 mem)))
&& i1 == i0+1 && i1 == i0+1
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh) && clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWload [i0] {s} p mem)) -> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWload [i0] {s} p0 mem))
(ORL (ORL
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p0 mem))
sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p1 mem))))
&& i1 == i0+2 && i1 == i0+2
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& r0.Uses == 1 && r0.Uses == 1
&& r1.Uses == 1 && r1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh) && clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLload [i0] {s} p mem)) -> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLload [i0] {s} p0 mem))
(ORQ (ORQ
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem)) r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p0 mem))
sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem)))) sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p1 mem))))
&& i1 == i0+2 && i1 == i0+2
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& r0.Uses == 1 && r0.Uses == 1
&& r1.Uses == 1 && r1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh) && clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLload [i0] {s} p mem)) -> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLload [i0] {s} p0 mem))
(ORQ (ORQ
r1:(BSWAPL x1:(MOVLload [i1] {s} p mem)) r1:(BSWAPL x1:(MOVLload [i1] {s} p0 mem))
sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p mem)))) sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p1 mem))))
&& i1 == i0+4 && i1 == i0+4
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& r0.Uses == 1 && r0.Uses == 1
&& r1.Uses == 1 && r1.Uses == 1
&& sh.Uses == 1 && sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil && mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh) && clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPQ <v.Type> (MOVQload [i0] {s} p mem)) -> @mergePoint(b,x0,x1) (BSWAPQ <v.Type> (MOVQload [i0] {s} p0 mem))
(ORL (ORL
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem)) s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p0 mem))
or:(ORL or:(ORL
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem)) s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p1 mem))
y)) y))
&& i1 == i0+1 && i1 == i0+1
&& j1 == j0-8 && j1 == j0-8
@ -1832,14 +1692,15 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or) && clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWload [i0] {s} p mem))) y) -> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWload [i0] {s} p0 mem))) y)
(ORQ (ORQ
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem)) s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p0 mem))
or:(ORQ or:(ORQ
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem)) s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p1 mem))
y)) y))
&& i1 == i0+1 && i1 == i0+1
&& j1 == j0-8 && j1 == j0-8
@ -1849,14 +1710,15 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or) && clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWload [i0] {s} p mem))) y) -> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWload [i0] {s} p0 mem))) y)
(ORQ (ORQ
s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem))) s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p0 mem)))
or:(ORQ or:(ORQ
s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem))) s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p1 mem)))
y)) y))
&& i1 == i0+2 && i1 == i0+2
&& j1 == j0-16 && j1 == j0-16
@ -1868,168 +1730,41 @@
&& s0.Uses == 1 && s0.Uses == 1
&& s1.Uses == 1 && s1.Uses == 1
&& or.Uses == 1 && or.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1,y) != nil && mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, r0, r1, s0, s1, or) && clobber(x0, x1, r0, r1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (BSWAPL <typ.UInt32> (MOVLload [i0] {s} p mem))) y) -> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (BSWAPL <typ.UInt32> (MOVLload [i0] {s} p0 mem))) y)
// Big-endian indexed loads
(ORL
x1:(MOVBloadidx1 [i1] {s} p idx mem)
sh:(SHLLconst [8] x0:(MOVBloadidx1 [i0] {s} p idx mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWloadidx1 [i0] {s} p idx mem))
(ORQ
x1:(MOVBloadidx1 [i1] {s} p idx mem)
sh:(SHLQconst [8] x0:(MOVBloadidx1 [i0] {s} p idx mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (ROLWconst <v.Type> [8] (MOVWloadidx1 [i0] {s} p idx mem))
(ORL
r1:(ROLWconst [8] x1:(MOVWloadidx1 [i1] {s} p idx mem))
sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWloadidx1 [i0] {s} p idx mem))))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLloadidx1 [i0] {s} p idx mem))
(ORQ
r1:(ROLWconst [8] x1:(MOVWloadidx1 [i1] {s} p idx mem))
sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWloadidx1 [i0] {s} p idx mem))))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPL <v.Type> (MOVLloadidx1 [i0] {s} p idx mem))
(ORQ
r1:(BSWAPL x1:(MOVLloadidx1 [i1] {s} p idx mem))
sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLloadidx1 [i0] {s} p idx mem))))
&& i1 == i0+4
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, r0, r1, sh)
-> @mergePoint(b,x0,x1) (BSWAPQ <v.Type> (MOVQloadidx1 [i0] {s} p idx mem))
(ORL
s0:(SHLLconst [j0] x0:(MOVBloadidx1 [i0] {s} p idx mem))
or:(ORL
s1:(SHLLconst [j1] x1:(MOVBloadidx1 [i1] {s} p idx mem))
y))
&& i1 == i0+1
&& j1 == j0-8
&& j1 % 16 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORL <v.Type> (SHLLconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWloadidx1 [i0] {s} p idx mem))) y)
(ORQ
s0:(SHLQconst [j0] x0:(MOVBloadidx1 [i0] {s} p idx mem))
or:(ORQ
s1:(SHLQconst [j1] x1:(MOVBloadidx1 [i1] {s} p idx mem))
y))
&& i1 == i0+1
&& j1 == j0-8
&& j1 % 16 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (ROLWconst <typ.UInt16> [8] (MOVWloadidx1 [i0] {s} p idx mem))) y)
(ORQ
s0:(SHLQconst [j0] r0:(ROLWconst [8] x0:(MOVWloadidx1 [i0] {s} p idx mem)))
or:(ORQ
s1:(SHLQconst [j1] r1:(ROLWconst [8] x1:(MOVWloadidx1 [i1] {s} p idx mem)))
y))
&& i1 == i0+2
&& j1 == j0-16
&& j1 % 32 == 0
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& mergePoint(b,x0,x1,y) != nil
&& clobber(x0, x1, r0, r1, s0, s1, or)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (BSWAPL <typ.UInt32> (MOVLloadidx1 [i0] {s} p idx mem))) y)
// Combine 2 byte stores + shift into rolw 8 + word store // Combine 2 byte stores + shift into rolw 8 + word store
(MOVBstore [i] {s} p w (MOVBstore [i] {s} p1 w
x0:(MOVBstore [i-1] {s} p (SHRWconst [8] w) mem)) x0:(MOVBstore [i-1] {s} p0 (SHRWconst [8] w) mem))
&& x0.Uses == 1 && x0.Uses == 1
&& same(p0, p1, 1)
&& clobber(x0) && clobber(x0)
-> (MOVWstore [i-1] {s} p (ROLWconst <w.Type> [8] w) mem) -> (MOVWstore [i-1] {s} p0 (ROLWconst <w.Type> [8] w) mem)
(MOVBstoreidx1 [i] {s} p idx w
x0:(MOVBstoreidx1 [i-1] {s} p idx (SHRWconst [8] w) mem))
&& x0.Uses == 1
&& clobber(x0)
-> (MOVWstoreidx1 [i-1] {s} p idx (ROLWconst <w.Type> [8] w) mem)
// Combine stores + shifts into bswap and larger (unaligned) stores // Combine stores + shifts into bswap and larger (unaligned) stores
(MOVBstore [i] {s} p w (MOVBstore [i] {s} p3 w
x2:(MOVBstore [i-1] {s} p (SHRLconst [8] w) x2:(MOVBstore [i-1] {s} p2 (SHRLconst [8] w)
x1:(MOVBstore [i-2] {s} p (SHRLconst [16] w) x1:(MOVBstore [i-2] {s} p1 (SHRLconst [16] w)
x0:(MOVBstore [i-3] {s} p (SHRLconst [24] w) mem)))) x0:(MOVBstore [i-3] {s} p0 (SHRLconst [24] w) mem))))
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& x2.Uses == 1 && x2.Uses == 1
&& same(p0, p1, 1)
&& same(p1, p2, 1)
&& same(p2, p3, 1)
&& clobber(x0, x1, x2) && clobber(x0, x1, x2)
-> (MOVLstore [i-3] {s} p (BSWAPL <w.Type> w) mem) -> (MOVLstore [i-3] {s} p0 (BSWAPL <w.Type> w) mem)
(MOVBstoreidx1 [i] {s} p idx w (MOVBstore [i] {s} p7 w
x2:(MOVBstoreidx1 [i-1] {s} p idx (SHRLconst [8] w) x6:(MOVBstore [i-1] {s} p6 (SHRQconst [8] w)
x1:(MOVBstoreidx1 [i-2] {s} p idx (SHRLconst [16] w) x5:(MOVBstore [i-2] {s} p5 (SHRQconst [16] w)
x0:(MOVBstoreidx1 [i-3] {s} p idx (SHRLconst [24] w) mem)))) x4:(MOVBstore [i-3] {s} p4 (SHRQconst [24] w)
&& x0.Uses == 1 x3:(MOVBstore [i-4] {s} p3 (SHRQconst [32] w)
&& x1.Uses == 1 x2:(MOVBstore [i-5] {s} p2 (SHRQconst [40] w)
&& x2.Uses == 1 x1:(MOVBstore [i-6] {s} p1 (SHRQconst [48] w)
&& clobber(x0, x1, x2) x0:(MOVBstore [i-7] {s} p0 (SHRQconst [56] w) mem))))))))
-> (MOVLstoreidx1 [i-3] {s} p idx (BSWAPL <w.Type> w) mem)
(MOVBstore [i] {s} p w
x6:(MOVBstore [i-1] {s} p (SHRQconst [8] w)
x5:(MOVBstore [i-2] {s} p (SHRQconst [16] w)
x4:(MOVBstore [i-3] {s} p (SHRQconst [24] w)
x3:(MOVBstore [i-4] {s} p (SHRQconst [32] w)
x2:(MOVBstore [i-5] {s} p (SHRQconst [40] w)
x1:(MOVBstore [i-6] {s} p (SHRQconst [48] w)
x0:(MOVBstore [i-7] {s} p (SHRQconst [56] w) mem))))))))
&& x0.Uses == 1 && x0.Uses == 1
&& x1.Uses == 1 && x1.Uses == 1
&& x2.Uses == 1 && x2.Uses == 1
@ -2037,165 +1772,99 @@
&& x4.Uses == 1 && x4.Uses == 1
&& x5.Uses == 1 && x5.Uses == 1
&& x6.Uses == 1 && x6.Uses == 1
&& same(p0, p1, 1)
&& same(p1, p2, 1)
&& same(p2, p3, 1)
&& same(p3, p4, 1)
&& same(p4, p5, 1)
&& same(p5, p6, 1)
&& same(p6, p7, 1)
&& clobber(x0, x1, x2, x3, x4, x5, x6) && clobber(x0, x1, x2, x3, x4, x5, x6)
-> (MOVQstore [i-7] {s} p (BSWAPQ <w.Type> w) mem) -> (MOVQstore [i-7] {s} p0 (BSWAPQ <w.Type> w) mem)
(MOVBstoreidx1 [i] {s} p idx w
x6:(MOVBstoreidx1 [i-1] {s} p idx (SHRQconst [8] w)
x5:(MOVBstoreidx1 [i-2] {s} p idx (SHRQconst [16] w)
x4:(MOVBstoreidx1 [i-3] {s} p idx (SHRQconst [24] w)
x3:(MOVBstoreidx1 [i-4] {s} p idx (SHRQconst [32] w)
x2:(MOVBstoreidx1 [i-5] {s} p idx (SHRQconst [40] w)
x1:(MOVBstoreidx1 [i-6] {s} p idx (SHRQconst [48] w)
x0:(MOVBstoreidx1 [i-7] {s} p idx (SHRQconst [56] w) mem))))))))
&& x0.Uses == 1
&& x1.Uses == 1
&& x2.Uses == 1
&& x3.Uses == 1
&& x4.Uses == 1
&& x5.Uses == 1
&& x6.Uses == 1
&& clobber(x0, x1, x2, x3, x4, x5, x6)
-> (MOVQstoreidx1 [i-7] {s} p idx (BSWAPQ <w.Type> w) mem)
// Combine constant stores into larger (unaligned) stores. // Combine constant stores into larger (unaligned) stores.
(MOVBstoreconst [c] {s} p x:(MOVBstoreconst [a] {s} p mem)) (MOVBstoreconst [c] {s} p1 x:(MOVBstoreconst [a] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem) -> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem)) (MOVBstoreconst [a] {s} p1 x:(MOVBstoreconst [c] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off() && ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem) -> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem)) (MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem) -> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem)) (MOVWstoreconst [a] {s} p1 x:(MOVWstoreconst [c] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off() && ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem) -> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem)) (MOVLstoreconst [c] {s} p1 x:(MOVLstoreconst [a] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem) -> (MOVQstore [ValAndOff(a).Off()] {s} p0 (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem)) (MOVLstoreconst [a] {s} p1 x:(MOVLstoreconst [c] {s} p0 mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off() && ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x) && clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem) -> (MOVQstore [ValAndOff(a).Off()] {s} p0 (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem)) (MOVQstoreconst [c] {s} p1 x:(MOVQstoreconst [c2] {s} p0 mem))
&& config.useSSE && config.useSSE
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(c2).Off() + 8 == ValAndOff(c).Off() && ValAndOff(c2).Off() + 8 == ValAndOff(c).Off()
&& ValAndOff(c).Val() == 0 && ValAndOff(c).Val() == 0
&& ValAndOff(c2).Val() == 0 && ValAndOff(c2).Val() == 0
&& clobber(x) && clobber(x)
-> (MOVOstore [ValAndOff(c2).Off()] {s} p (MOVOconst [0]) mem) -> (MOVOstore [ValAndOff(c2).Off()] {s} p0 (MOVOconst [0]) mem)
(MOVBstoreconstidx1 [c] {s} p i x:(MOVBstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p i mem)
(MOVWstoreconstidx1 [c] {s} p i x:(MOVWstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p i mem)
(MOVLstoreconstidx1 [c] {s} p i x:(MOVLstoreconstidx1 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p i (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVWstoreconstidx2 [c] {s} p i x:(MOVWstoreconstidx2 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconstidx1 [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p (SHLQconst <i.Type> [1] i) mem)
(MOVLstoreconstidx4 [c] {s} p i x:(MOVLstoreconstidx4 [a] {s} p i mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstoreidx1 [ValAndOff(a).Off()] {s} p (SHLQconst <i.Type> [2] i) (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
// Combine stores into larger (unaligned) stores. // Combine stores into larger (unaligned) stores.
(MOVBstore [i] {s} p (SHR(W|L|Q)const [8] w) x:(MOVBstore [i-1] {s} p w mem)) (MOVBstore [i] {s} p1 (SHR(W|L|Q)const [8] w) x:(MOVBstore [i-1] {s} p0 w mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i-1] {s} p w mem) -> (MOVWstore [i-1] {s} p0 w mem)
(MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHR(W|L|Q)const [8] w) mem)) (MOVBstore [i] {s} p1 w x:(MOVBstore [i+1] {s} p0 (SHR(W|L|Q)const [8] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i] {s} p w mem) -> (MOVWstore [i] {s} p0 w mem)
(MOVBstore [i] {s} p (SHR(L|Q)const [j] w) x:(MOVBstore [i-1] {s} p w0:(SHR(L|Q)const [j-8] w) mem)) (MOVBstore [i] {s} p1 (SHR(L|Q)const [j] w) x:(MOVBstore [i-1] {s} p0 w0:(SHR(L|Q)const [j-8] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVWstore [i-1] {s} p w0 mem) -> (MOVWstore [i-1] {s} p0 w0 mem)
(MOVWstore [i] {s} p (SHR(L|Q)const [16] w) x:(MOVWstore [i-2] {s} p w mem)) (MOVWstore [i] {s} p1 (SHR(L|Q)const [16] w) x:(MOVWstore [i-2] {s} p0 w mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstore [i-2] {s} p w mem) -> (MOVLstore [i-2] {s} p0 w mem)
(MOVWstore [i] {s} p (SHR(L|Q)const [j] w) x:(MOVWstore [i-2] {s} p w0:(SHR(L|Q)const [j-16] w) mem)) (MOVWstore [i] {s} p1 (SHR(L|Q)const [j] w) x:(MOVWstore [i-2] {s} p0 w0:(SHR(L|Q)const [j-16] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVLstore [i-2] {s} p w0 mem) -> (MOVLstore [i-2] {s} p0 w0 mem)
(MOVLstore [i] {s} p (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p w mem)) (MOVLstore [i] {s} p1 (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p0 w mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVQstore [i-4] {s} p w mem) -> (MOVQstore [i-4] {s} p0 w mem)
(MOVLstore [i] {s} p (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p w0:(SHRQconst [j-32] w) mem)) (MOVLstore [i] {s} p1 (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p0 w0:(SHRQconst [j-32] w) mem))
&& x.Uses == 1 && x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x) && clobber(x)
-> (MOVQstore [i-4] {s} p w0 mem) -> (MOVQstore [i-4] {s} p0 w0 mem)
(MOVBstoreidx1 [i] {s} p idx (SHR(W|L|Q)const [8] w) x:(MOVBstoreidx1 [i-1] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i-1] {s} p idx w mem)
(MOVBstoreidx1 [i] {s} p idx (SHR(L|Q)const [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHR(L|Q)const [j-8] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i-1] {s} p idx w0 mem)
(MOVWstoreidx1 [i] {s} p idx (SHR(L|Q)const [16] w) x:(MOVWstoreidx1 [i-2] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p idx w mem)
(MOVWstoreidx1 [i] {s} p idx (SHR(L|Q)const [j] w) x:(MOVWstoreidx1 [i-2] {s} p idx w0:(SHR(L|Q)const [j-16] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p idx w0 mem)
(MOVLstoreidx1 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx1 [i-4] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVQstoreidx1 [i-4] {s} p idx w mem)
(MOVLstoreidx1 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx1 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVQstoreidx1 [i-4] {s} p idx w0 mem)
(MOVWstoreidx2 [i] {s} p idx (SHR(L|Q)const [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w mem)
(MOVWstoreidx2 [i] {s} p idx (SHRQconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRQconst [j-16] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p (SHLQconst <idx.Type> [1] idx) w0 mem)
(MOVLstoreidx4 [i] {s} p idx (SHRQconst [32] w) x:(MOVLstoreidx4 [i-4] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w mem)
(MOVLstoreidx4 [i] {s} p idx (SHRQconst [j] w) x:(MOVLstoreidx4 [i-4] {s} p idx w0:(SHRQconst [j-32] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVQstoreidx1 [i-4] {s} p (SHLQconst <idx.Type> [2] idx) w0 mem)
(MOVBstore [i] {s} p (MOVBstore [i] {s} p
x1:(MOVBload [j] {s2} p2 mem) x1:(MOVBload [j] {s2} p2 mem)
@ -2320,41 +1989,6 @@
(BSFQ (ORQconst <t> [1<<8] (MOVBQZX x))) -> (BSFQ (ORQconst <t> [1<<8] x)) (BSFQ (ORQconst <t> [1<<8] (MOVBQZX x))) -> (BSFQ (ORQconst <t> [1<<8] x))
(BSFQ (ORQconst <t> [1<<16] (MOVWQZX x))) -> (BSFQ (ORQconst <t> [1<<16] x)) (BSFQ (ORQconst <t> [1<<16] (MOVWQZX x))) -> (BSFQ (ORQconst <t> [1<<16] x))
// Simplify indexed loads/stores
(MOVBstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVBstore [i+c] {s} p w mem)
(MOVWstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVWstore [i+c] {s} p w mem)
(MOVLstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVLstore [i+c] {s} p w mem)
(MOVQstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVQstore [i+c] {s} p w mem)
(MOVWstoreidx2 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+2*c) -> (MOVWstore [i+2*c] {s} p w mem)
(MOVLstoreidx4 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+4*c) -> (MOVLstore [i+4*c] {s} p w mem)
(MOVLstoreidx8 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+8*c) -> (MOVLstore [i+8*c] {s} p w mem)
(MOVQstoreidx8 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+8*c) -> (MOVQstore [i+8*c] {s} p w mem)
(MOVSSstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVSSstore [i+c] {s} p w mem)
(MOVSSstoreidx4 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+4*c) -> (MOVSSstore [i+4*c] {s} p w mem)
(MOVSDstoreidx1 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+c) -> (MOVSDstore [i+c] {s} p w mem)
(MOVSDstoreidx8 [i] {s} p (MOVQconst [c]) w mem) && is32Bit(i+8*c) -> (MOVSDstore [i+8*c] {s} p w mem)
(MOVBloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVBload [i+c] {s} p mem)
(MOVWloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVWload [i+c] {s} p mem)
(MOVLloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVLload [i+c] {s} p mem)
(MOVQloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVQload [i+c] {s} p mem)
(MOVWloadidx2 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+2*c) -> (MOVWload [i+2*c] {s} p mem)
(MOVLloadidx4 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+4*c) -> (MOVLload [i+4*c] {s} p mem)
(MOVLloadidx8 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+8*c) -> (MOVLload [i+8*c] {s} p mem)
(MOVQloadidx8 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+8*c) -> (MOVQload [i+8*c] {s} p mem)
(MOVSSloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVSSload [i+c] {s} p mem)
(MOVSSloadidx4 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+4*c) -> (MOVSSload [i+4*c] {s} p mem)
(MOVSDloadidx1 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+c) -> (MOVSDload [i+c] {s} p mem)
(MOVSDloadidx8 [i] {s} p (MOVQconst [c]) mem) && is32Bit(i+8*c) -> (MOVSDload [i+8*c] {s} p mem)
// Combine consts into storeidx.
// Note that when c == 0, it takes more bytes to encode
// the immediate $0 than to zero a register and use it.
// We do the rewrite anyway, to minimize register pressure.
(MOVBstoreidx1 [off] {s} ptr idx (MOVLconst [c]) mem) && validValAndOff(int64(int8(c)), off) -> (MOVBstoreconstidx1 [makeValAndOff(int64(int8(c)), off)] {s} ptr idx mem)
(MOVWstoreidx(1|2) [off] {s} ptr idx (MOVLconst [c]) mem) && validValAndOff(int64(int16(c)), off) -> (MOVWstoreconstidx(1|2) [makeValAndOff(int64(int16(c)), off)] {s} ptr idx mem)
(MOVLstoreidx(1|4) [off] {s} ptr idx (MOVQconst [c]) mem) && validValAndOff(int64(int32(c)), off) -> (MOVLstoreconstidx(1|4) [makeValAndOff(int64(int32(c)), off)] {s} ptr idx mem)
(MOVQstoreidx(1|8) [off] {s} ptr idx (MOVQconst [c]) mem) && validValAndOff(c, off) -> (MOVQstoreconstidx(1|8) [makeValAndOff(c, off)] {s} ptr idx mem)
// Redundant sign/zero extensions // Redundant sign/zero extensions
// Note: see issue 21963. We have to make sure we use the right type on // Note: see issue 21963. We have to make sure we use the right type on
// the resulting extension (the outer type, not the inner type). // the resulting extension (the outer type, not the inner type).

View File

@ -245,6 +245,7 @@ dd.ssa-prog {
svg { svg {
cursor: default; cursor: default;
outline: 1px solid #eee; outline: 1px solid #eee;
width: 100%;
} }
body.darkmode { body.darkmode {
@ -975,7 +976,7 @@ func (d *dotWriter) writeFuncSVG(w io.Writer, phase string, f *Func) {
fmt.Println(err) fmt.Println(err)
return return
} }
fmt.Fprint(pipe, `digraph "" { margin=0; size="4,40"; ranksep=.2; `) fmt.Fprint(pipe, `digraph "" { margin=0; ranksep=.2; `)
id := strings.Replace(phase, " ", "-", -1) id := strings.Replace(phase, " ", "-", -1)
fmt.Fprintf(pipe, `id="g_graph_%s";`, id) fmt.Fprintf(pipe, `id="g_graph_%s";`, id)
fmt.Fprintf(pipe, `node [style=filled,fillcolor=white,fontsize=16,fontname="Menlo,Times,serif",margin="0.01,0.03"];`) fmt.Fprintf(pipe, `node [style=filled,fillcolor=white,fontsize=16,fontname="Menlo,Times,serif",margin="0.01,0.03"];`)

View File

@ -1305,9 +1305,9 @@ func isNonNegative(v *Value) bool {
if !v.Type.IsInteger() { if !v.Type.IsInteger() {
panic("isNonNegative bad type") panic("isNonNegative bad type")
} }
if !v.Type.IsSigned() { // TODO: return true if !v.Type.IsSigned()
return true // SSA isn't type-safe enough to do that now (issue 37753).
} // The checks below depend only on the pattern of bits.
switch v.Op { switch v.Op {
case OpConst64: case OpConst64:

View File

@ -1247,3 +1247,43 @@ func read64(sym interface{}, off int64, byteorder binary.ByteOrder) uint64 {
copy(buf, src) copy(buf, src)
return byteorder.Uint64(buf) return byteorder.Uint64(buf)
} }
// same reports whether x and y are the same value.
// It checks to a maximum depth of d, so it may report
// a false negative.
func same(x, y *Value, depth int) bool {
if x == y {
return true
}
if depth <= 0 {
return false
}
if x.Op != y.Op || x.Aux != y.Aux || x.AuxInt != y.AuxInt {
return false
}
if len(x.Args) != len(y.Args) {
return false
}
if opcodeTable[x.Op].commutative {
// Check exchanged ordering first.
for i, a := range x.Args {
j := i
if j < 2 {
j ^= 1
}
b := y.Args[j]
if !same(a, b, depth-1) {
goto checkNormalOrder
}
}
return true
checkNormalOrder:
}
for i, a := range x.Args {
b := y.Args[i]
if !same(a, b, depth-1) {
return false
}
}
return true
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -50,16 +50,23 @@ func (s *scanner) init(src io.Reader, errh func(line, col uint, msg string), mod
// errorf reports an error at the most recently read character position. // errorf reports an error at the most recently read character position.
func (s *scanner) errorf(format string, args ...interface{}) { func (s *scanner) errorf(format string, args ...interface{}) {
s.bad = true
s.error(fmt.Sprintf(format, args...)) s.error(fmt.Sprintf(format, args...))
} }
// errorAtf reports an error at a byte column offset relative to the current token start. // errorAtf reports an error at a byte column offset relative to the current token start.
func (s *scanner) errorAtf(offset int, format string, args ...interface{}) { func (s *scanner) errorAtf(offset int, format string, args ...interface{}) {
s.bad = true
s.errh(s.line, s.col+uint(offset), fmt.Sprintf(format, args...)) s.errh(s.line, s.col+uint(offset), fmt.Sprintf(format, args...))
} }
// setLit sets the scanner state for a recognized _Literal token.
func (s *scanner) setLit(kind LitKind, ok bool) {
s.nlsemi = true
s.tok = _Literal
s.lit = string(s.segment())
s.bad = !ok
s.kind = kind
}
// next advances the scanner by reading the next token. // next advances the scanner by reading the next token.
// //
// If a read, source encoding, or lexical error occurs, next calls // If a read, source encoding, or lexical error occurs, next calls
@ -461,8 +468,8 @@ func (s *scanner) digits(base int, invalid *int) (digsep int) {
} }
func (s *scanner) number(seenPoint bool) { func (s *scanner) number(seenPoint bool) {
s.bad = false ok := true
kind := IntLit
base := 10 // number base base := 10 // number base
prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b' prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
digsep := 0 // bit 0: digit present, bit 1: '_' present digsep := 0 // bit 0: digit present, bit 1: '_' present
@ -470,7 +477,6 @@ func (s *scanner) number(seenPoint bool) {
// integer part // integer part
if !seenPoint { if !seenPoint {
s.kind = IntLit
if s.ch == '0' { if s.ch == '0' {
s.nextch() s.nextch()
switch lower(s.ch) { switch lower(s.ch) {
@ -491,7 +497,8 @@ func (s *scanner) number(seenPoint bool) {
digsep |= s.digits(base, &invalid) digsep |= s.digits(base, &invalid)
if s.ch == '.' { if s.ch == '.' {
if prefix == 'o' || prefix == 'b' { if prefix == 'o' || prefix == 'b' {
s.errorf("invalid radix point in %s", litname(prefix)) s.errorf("invalid radix point in %s literal", baseName(base))
ok = false
} }
s.nextch() s.nextch()
seenPoint = true seenPoint = true
@ -500,68 +507,77 @@ func (s *scanner) number(seenPoint bool) {
// fractional part // fractional part
if seenPoint { if seenPoint {
s.kind = FloatLit kind = FloatLit
digsep |= s.digits(base, &invalid) digsep |= s.digits(base, &invalid)
} }
if digsep&1 == 0 && !s.bad { if digsep&1 == 0 && ok {
s.errorf("%s has no digits", litname(prefix)) s.errorf("%s literal has no digits", baseName(base))
ok = false
} }
// exponent // exponent
if e := lower(s.ch); e == 'e' || e == 'p' { if e := lower(s.ch); e == 'e' || e == 'p' {
if !s.bad { if ok {
switch { switch {
case e == 'e' && prefix != 0 && prefix != '0': case e == 'e' && prefix != 0 && prefix != '0':
s.errorf("%q exponent requires decimal mantissa", s.ch) s.errorf("%q exponent requires decimal mantissa", s.ch)
ok = false
case e == 'p' && prefix != 'x': case e == 'p' && prefix != 'x':
s.errorf("%q exponent requires hexadecimal mantissa", s.ch) s.errorf("%q exponent requires hexadecimal mantissa", s.ch)
ok = false
} }
} }
s.nextch() s.nextch()
s.kind = FloatLit kind = FloatLit
if s.ch == '+' || s.ch == '-' { if s.ch == '+' || s.ch == '-' {
s.nextch() s.nextch()
} }
digsep = s.digits(10, nil) | digsep&2 // don't lose sep bit digsep = s.digits(10, nil) | digsep&2 // don't lose sep bit
if digsep&1 == 0 && !s.bad { if digsep&1 == 0 && ok {
s.errorf("exponent has no digits") s.errorf("exponent has no digits")
ok = false
} }
} else if prefix == 'x' && s.kind == FloatLit && !s.bad { } else if prefix == 'x' && kind == FloatLit && ok {
s.errorf("hexadecimal mantissa requires a 'p' exponent") s.errorf("hexadecimal mantissa requires a 'p' exponent")
ok = false
} }
// suffix 'i' // suffix 'i'
if s.ch == 'i' { if s.ch == 'i' {
s.kind = ImagLit kind = ImagLit
s.nextch() s.nextch()
} }
s.nlsemi = true s.setLit(kind, ok) // do this now so we can use s.lit below
s.lit = string(s.segment())
s.tok = _Literal
if s.kind == IntLit && invalid >= 0 && !s.bad { if kind == IntLit && invalid >= 0 && ok {
s.errorAtf(invalid, "invalid digit %q in %s", s.lit[invalid], litname(prefix)) s.errorAtf(invalid, "invalid digit %q in %s literal", s.lit[invalid], baseName(base))
ok = false
} }
if digsep&2 != 0 && !s.bad { if digsep&2 != 0 && ok {
if i := invalidSep(s.lit); i >= 0 { if i := invalidSep(s.lit); i >= 0 {
s.errorAtf(i, "'_' must separate successive digits") s.errorAtf(i, "'_' must separate successive digits")
} ok = false
} }
} }
func litname(prefix rune) string { s.bad = !ok // correct s.bad
switch prefix {
case 'x':
return "hexadecimal literal"
case 'o', '0':
return "octal literal"
case 'b':
return "binary literal"
} }
return "decimal literal"
func baseName(base int) string {
switch base {
case 2:
return "binary"
case 8:
return "octal"
case 10:
return "decimal"
case 16:
return "hexadecimal"
}
panic("invalid base")
} }
// invalidSep returns the index of the first invalid separator in x, or -1. // invalidSep returns the index of the first invalid separator in x, or -1.
@ -605,17 +621,19 @@ func invalidSep(x string) int {
} }
func (s *scanner) rune() { func (s *scanner) rune() {
s.bad = false ok := true
s.nextch() s.nextch()
n := 0 n := 0
for ; ; n++ { for ; ; n++ {
if s.ch == '\'' { if s.ch == '\'' {
if !s.bad { if ok {
if n == 0 { if n == 0 {
s.errorf("empty rune literal or unescaped '") s.errorf("empty rune literal or unescaped '")
ok = false
} else if n != 1 { } else if n != 1 {
s.errorAtf(0, "more than one character in rune literal") s.errorAtf(0, "more than one character in rune literal")
ok = false
} }
} }
s.nextch() s.nextch()
@ -623,32 +641,33 @@ func (s *scanner) rune() {
} }
if s.ch == '\\' { if s.ch == '\\' {
s.nextch() s.nextch()
s.escape('\'') if !s.escape('\'') {
ok = false
}
continue continue
} }
if s.ch == '\n' { if s.ch == '\n' {
if !s.bad { if ok {
s.errorf("newline in rune literal") s.errorf("newline in rune literal")
ok = false
} }
break break
} }
if s.ch < 0 { if s.ch < 0 {
if !s.bad { if ok {
s.errorAtf(0, "rune literal not terminated") s.errorAtf(0, "rune literal not terminated")
ok = false
} }
break break
} }
s.nextch() s.nextch()
} }
s.nlsemi = true s.setLit(RuneLit, ok)
s.lit = string(s.segment())
s.kind = RuneLit
s.tok = _Literal
} }
func (s *scanner) stdString() { func (s *scanner) stdString() {
s.bad = false ok := true
s.nextch() s.nextch()
for { for {
@ -658,28 +677,29 @@ func (s *scanner) stdString() {
} }
if s.ch == '\\' { if s.ch == '\\' {
s.nextch() s.nextch()
s.escape('"') if !s.escape('"') {
ok = false
}
continue continue
} }
if s.ch == '\n' { if s.ch == '\n' {
s.errorf("newline in string") s.errorf("newline in string")
ok = false
break break
} }
if s.ch < 0 { if s.ch < 0 {
s.errorAtf(0, "string not terminated") s.errorAtf(0, "string not terminated")
ok = false
break break
} }
s.nextch() s.nextch()
} }
s.nlsemi = true s.setLit(StringLit, ok)
s.lit = string(s.segment())
s.kind = StringLit
s.tok = _Literal
} }
func (s *scanner) rawString() { func (s *scanner) rawString() {
s.bad = false ok := true
s.nextch() s.nextch()
for { for {
@ -689,6 +709,7 @@ func (s *scanner) rawString() {
} }
if s.ch < 0 { if s.ch < 0 {
s.errorAtf(0, "string not terminated") s.errorAtf(0, "string not terminated")
ok = false
break break
} }
s.nextch() s.nextch()
@ -697,10 +718,7 @@ func (s *scanner) rawString() {
// literal (even though they are not part of the literal // literal (even though they are not part of the literal
// value). // value).
s.nlsemi = true s.setLit(StringLit, ok)
s.lit = string(s.segment())
s.kind = StringLit
s.tok = _Literal
} }
func (s *scanner) comment(text string) { func (s *scanner) comment(text string) {
@ -797,14 +815,14 @@ func (s *scanner) fullComment() {
} }
} }
func (s *scanner) escape(quote rune) { func (s *scanner) escape(quote rune) bool {
var n int var n int
var base, max uint32 var base, max uint32
switch s.ch { switch s.ch {
case quote, 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\': case quote, 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\':
s.nextch() s.nextch()
return return true
case '0', '1', '2', '3', '4', '5', '6', '7': case '0', '1', '2', '3', '4', '5', '6', '7':
n, base, max = 3, 8, 255 n, base, max = 3, 8, 255
case 'x': case 'x':
@ -818,16 +836,16 @@ func (s *scanner) escape(quote rune) {
n, base, max = 8, 16, unicode.MaxRune n, base, max = 8, 16, unicode.MaxRune
default: default:
if s.ch < 0 { if s.ch < 0 {
return // complain in caller about EOF return true // complain in caller about EOF
} }
s.errorf("unknown escape") s.errorf("unknown escape")
return return false
} }
var x uint32 var x uint32
for i := n; i > 0; i-- { for i := n; i > 0; i-- {
if s.ch < 0 { if s.ch < 0 {
return // complain in caller about EOF return true // complain in caller about EOF
} }
d := base d := base
if isDecimal(s.ch) { if isDecimal(s.ch) {
@ -836,12 +854,8 @@ func (s *scanner) escape(quote rune) {
d = uint32(lower(s.ch)) - 'a' + 10 d = uint32(lower(s.ch)) - 'a' + 10
} }
if d >= base { if d >= base {
kind := "hex" s.errorf("invalid character %q in %s escape", s.ch, baseName(int(base)))
if base == 8 { return false
kind = "octal"
}
s.errorf("invalid character %q in %s escape", s.ch, kind)
return
} }
// d < base // d < base
x = x*base + d x = x*base + d
@ -850,10 +864,13 @@ func (s *scanner) escape(quote rune) {
if x > max && base == 8 { if x > max && base == 8 {
s.errorf("octal escape value %d > 255", x) s.errorf("octal escape value %d > 255", x)
return return false
} }
if x > max || 0xD800 <= x && x < 0xE000 /* surrogate range */ { if x > max || 0xD800 <= x && x < 0xE000 /* surrogate range */ {
s.errorf("escape is invalid Unicode code point %#U", x) s.errorf("escape is invalid Unicode code point %#U", x)
return false
} }
return true
} }

View File

@ -613,9 +613,9 @@ func TestScanErrors(t *testing.T) {
{`'\`, "rune literal not terminated", 0, 0}, {`'\`, "rune literal not terminated", 0, 0},
{`'\'`, "rune literal not terminated", 0, 0}, {`'\'`, "rune literal not terminated", 0, 0},
{`'\x`, "rune literal not terminated", 0, 0}, {`'\x`, "rune literal not terminated", 0, 0},
{`'\x'`, "invalid character '\\'' in hex escape", 0, 3}, {`'\x'`, "invalid character '\\'' in hexadecimal escape", 0, 3},
{`'\y'`, "unknown escape", 0, 2}, {`'\y'`, "unknown escape", 0, 2},
{`'\x0'`, "invalid character '\\'' in hex escape", 0, 4}, {`'\x0'`, "invalid character '\\'' in hexadecimal escape", 0, 4},
{`'\00'`, "invalid character '\\'' in octal escape", 0, 4}, {`'\00'`, "invalid character '\\'' in octal escape", 0, 4},
{`'\377' /*`, "comment not terminated", 0, 7}, // valid octal escape {`'\377' /*`, "comment not terminated", 0, 7}, // valid octal escape
{`'\378`, "invalid character '8' in octal escape", 0, 4}, {`'\378`, "invalid character '8' in octal escape", 0, 4},
@ -633,9 +633,9 @@ func TestScanErrors(t *testing.T) {
{`"\`, "string not terminated", 0, 0}, {`"\`, "string not terminated", 0, 0},
{`"\"`, "string not terminated", 0, 0}, {`"\"`, "string not terminated", 0, 0},
{`"\x`, "string not terminated", 0, 0}, {`"\x`, "string not terminated", 0, 0},
{`"\x"`, "invalid character '\"' in hex escape", 0, 3}, {`"\x"`, "invalid character '\"' in hexadecimal escape", 0, 3},
{`"\y"`, "unknown escape", 0, 2}, {`"\y"`, "unknown escape", 0, 2},
{`"\x0"`, "invalid character '\"' in hex escape", 0, 4}, {`"\x0"`, "invalid character '\"' in hexadecimal escape", 0, 4},
{`"\00"`, "invalid character '\"' in octal escape", 0, 4}, {`"\00"`, "invalid character '\"' in octal escape", 0, 4},
{`"\377" /*`, "comment not terminated", 0, 7}, // valid octal escape {`"\377" /*`, "comment not terminated", 0, 7}, // valid octal escape
{`"\378"`, "invalid character '8' in octal escape", 0, 4}, {`"\378"`, "invalid character '8' in octal escape", 0, 4},
@ -644,8 +644,8 @@ func TestScanErrors(t *testing.T) {
{`s := "foo\z"`, "unknown escape", 0, 10}, {`s := "foo\z"`, "unknown escape", 0, 10},
{`s := "foo\z00\nbar"`, "unknown escape", 0, 10}, {`s := "foo\z00\nbar"`, "unknown escape", 0, 10},
{`"\x`, "string not terminated", 0, 0}, {`"\x`, "string not terminated", 0, 0},
{`"\x"`, "invalid character '\"' in hex escape", 0, 3}, {`"\x"`, "invalid character '\"' in hexadecimal escape", 0, 3},
{`var s string = "\x"`, "invalid character '\"' in hex escape", 0, 18}, {`var s string = "\x"`, "invalid character '\"' in hexadecimal escape", 0, 18},
{`return "\Uffffffff"`, "escape is invalid Unicode code point U+FFFFFFFF", 0, 18}, {`return "\Uffffffff"`, "escape is invalid Unicode code point U+FFFFFFFF", 0, 18},
{"0b.0", "invalid radix point in binary literal", 0, 2}, {"0b.0", "invalid radix point in binary literal", 0, 2},
@ -687,6 +687,48 @@ func TestScanErrors(t *testing.T) {
} }
} }
func TestDirectives(t *testing.T) {
for _, src := range []string{
"line",
"// line",
"//line",
"//line foo",
"//line foo%bar",
"go",
"// go:",
"//go:",
"//go :foo",
"//go:foo",
"//go:foo%bar",
} {
got := ""
var s scanner
s.init(strings.NewReader(src), func(_, col uint, msg string) {
if col != colbase {
t.Errorf("%s: got col = %d; want %d", src, col, colbase)
}
if msg == "" {
t.Errorf("%s: handler called with empty msg", src)
}
got = msg
}, directives)
s.next()
if strings.HasPrefix(src, "//line ") || strings.HasPrefix(src, "//go:") {
// handler should have been called
if got != src {
t.Errorf("got %s; want %s", got, src)
}
} else {
// handler should not have been called
if got != "" {
t.Errorf("got %s for %s", got, src)
}
}
}
}
func TestIssue21938(t *testing.T) { func TestIssue21938(t *testing.T) {
s := "/*" + strings.Repeat(" ", 4089) + "*/ .5" s := "/*" + strings.Repeat(" ", 4089) + "*/ .5"

View File

@ -293,6 +293,11 @@ func (f *File) Visit(node ast.Node) ast.Visitor {
ast.Walk(f, n.Assign) ast.Walk(f, n.Assign)
return nil return nil
} }
case *ast.FuncDecl:
// Don't annotate functions with blank names - they cannot be executed.
if n.Name.Name == "_" {
return nil
}
} }
return f return f
} }

View File

@ -98,9 +98,11 @@ func (t *tester) run() {
os.Setenv("PATH", fmt.Sprintf("%s%c%s", gobin, os.PathListSeparator, os.Getenv("PATH"))) os.Setenv("PATH", fmt.Sprintf("%s%c%s", gobin, os.PathListSeparator, os.Getenv("PATH")))
} }
slurp, err := exec.Command("go", "env", "CGO_ENABLED").Output() cmd := exec.Command("go", "env", "CGO_ENABLED")
cmd.Stderr = new(bytes.Buffer)
slurp, err := cmd.Output()
if err != nil { if err != nil {
fatalf("Error running go env CGO_ENABLED: %v", err) fatalf("Error running go env CGO_ENABLED: %v\n%s", err, cmd.Stderr)
} }
t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp))) t.cgoEnabled, _ = strconv.ParseBool(strings.TrimSpace(string(slurp)))
if flag.NArg() > 0 && t.runRxStr != "" { if flag.NArg() > 0 && t.runRxStr != "" {

View File

@ -3,11 +3,10 @@ module cmd
go 1.14 go 1.14
require ( require (
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
golang.org/x/mod v0.2.0 golang.org/x/mod v0.2.0
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
) )

View File

@ -1,8 +1,9 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 h1:TgXhFz35pKlZuUz1pNlOKk1UCSXPpuUIc144Wd7SxCA= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff h1:k/MrR0lKiCokRu1JUDDAWhWZinfBAOZRzz3LkPOkFMs= golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff h1:k/MrR0lKiCokRu1JUDDAWhWZinfBAOZRzz3LkPOkFMs=
@ -12,12 +13,13 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -27,8 +29,8 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d h1:ZQ18He7VORO2x4IEBuwfdp56K+ftEzRjvL0cFuCGCcM= golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca h1:cFQHQhDv9N1vc+64dtXDAyd3exHDGfRTtveOnD0IsLI=
golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1044,6 +1044,7 @@ func TestGetGitDefaultBranch(t *testing.T) {
func TestAccidentalGitCheckout(t *testing.T) { func TestAccidentalGitCheckout(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git") testenv.MustHaveExecPath(t, "git")
testenv.MustHaveExecPath(t, "svn")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -2208,15 +2209,10 @@ func testBuildmodePIE(t *testing.T, useCgo bool) {
t.Fatal(err) t.Fatal(err)
} }
defer f.Close() defer f.Close()
const (
IMAGE_FILE_RELOCS_STRIPPED = 0x0001
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
)
if f.Section(".reloc") == nil { if f.Section(".reloc") == nil {
t.Error(".reloc section is not present") t.Error(".reloc section is not present")
} }
if (f.FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) != 0 { if (f.FileHeader.Characteristics & pe.IMAGE_FILE_RELOCS_STRIPPED) != 0 {
t.Error("IMAGE_FILE_RELOCS_STRIPPED flag is set") t.Error("IMAGE_FILE_RELOCS_STRIPPED flag is set")
} }
var dc uint16 var dc uint16
@ -2225,13 +2221,13 @@ func testBuildmodePIE(t *testing.T, useCgo bool) {
dc = oh.DllCharacteristics dc = oh.DllCharacteristics
case *pe.OptionalHeader64: case *pe.OptionalHeader64:
dc = oh.DllCharacteristics dc = oh.DllCharacteristics
if (dc & IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) == 0 { if (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) == 0 {
t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set") t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
} }
default: default:
t.Fatalf("unexpected optional header type of %T", f.OptionalHeader) t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
} }
if (dc & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 { if (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 {
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set") t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
} }
default: default:

View File

@ -10,10 +10,12 @@ import "net/http"
// AddCredentials fills in the user's credentials for req, if any. // AddCredentials fills in the user's credentials for req, if any.
// The return value reports whether any matching credentials were found. // The return value reports whether any matching credentials were found.
func AddCredentials(req *http.Request) (added bool) { func AddCredentials(req *http.Request) (added bool) {
host := req.URL.Hostname()
// TODO(golang.org/issue/26232): Support arbitrary user-provided credentials. // TODO(golang.org/issue/26232): Support arbitrary user-provided credentials.
netrcOnce.Do(readNetrc) netrcOnce.Do(readNetrc)
for _, l := range netrc { for _, l := range netrc {
if l.machine == req.URL.Host { if l.machine == host {
req.SetBasicAuth(l.login, l.password) req.SetBasicAuth(l.login, l.password)
return true return true
} }

View File

@ -13,19 +13,19 @@
// or an F_OFD_SETLK command for 'fcntl', that allows for better concurrency and // or an F_OFD_SETLK command for 'fcntl', that allows for better concurrency and
// does not require per-inode bookkeeping in the application. // does not require per-inode bookkeeping in the application.
// //
// TODO(bcmills): If we add a build tag for Illumos (see golang.org/issue/20603) // TODO(golang.org/issue/35618): add a syscall.Flock binding for Illumos and
// then Illumos should use F_OFD_SETLK, and the resulting code would be as // switch it over to use filelock_unix.go.
// simple as filelock_unix.go. We will still need the code in this file for AIX
// or as long as Oracle Solaris provides only F_SETLK.
package filelock package filelock
import ( import (
"errors" "errors"
"io" "io"
"math/rand"
"os" "os"
"sync" "sync"
"syscall" "syscall"
"time"
) )
type lockType int16 type lockType int16
@ -91,7 +91,67 @@ func lock(f File, lt lockType) (err error) {
wait <- f wait <- f
} }
// Spurious EDEADLK errors arise on platforms that compute deadlock graphs at
// the process, rather than thread, level. Consider processes P and Q, with
// threads P.1, P.2, and Q.3. The following trace is NOT a deadlock, but will be
// reported as a deadlock on systems that consider only process granularity:
//
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 blocks on file B. (This is erroneously reported as a deadlock.)
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 unblocks and locks file B.
// P.2 unlocks file B.
//
// These spurious errors were observed in practice on AIX and Solaris in
// cmd/go: see https://golang.org/issue/32817.
//
// We work around this bug by treating EDEADLK as always spurious. If there
// really is a lock-ordering bug between the interacting processes, it will
// become a livelock instead, but that's not appreciably worse than if we had
// a proper flock implementation (which generally does not even attempt to
// diagnose deadlocks).
//
// In the above example, that changes the trace to:
//
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 spuriously fails to lock file B and goes to sleep.
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 wakes up and locks file B.
// P.2 unlocks file B.
//
// We know that the retry loop will not introduce a *spurious* livelock
// because, according to the POSIX specification, EDEADLK is only to be
// returned when “the lock is blocked by a lock from another process”.
// If that process is blocked on some lock that we are holding, then the
// resulting livelock is due to a real deadlock (and would manifest as such
// when using, for example, the flock implementation of this package).
// If the other process is *not* blocked on some other lock that we are
// holding, then it will eventually release the requested lock.
nextSleep := 1 * time.Millisecond
const maxSleep = 500 * time.Millisecond
for {
err = setlkw(f.Fd(), lt) err = setlkw(f.Fd(), lt)
if err != syscall.EDEADLK {
break
}
time.Sleep(nextSleep)
nextSleep += nextSleep
if nextSleep > maxSleep {
nextSleep = maxSleep
}
// Apply 10% jitter to avoid synchronizing collisions when we finally unblock.
nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep))
}
if err != nil { if err != nil {
unlock(f) unlock(f)

View File

@ -8,8 +8,11 @@
package lockedfile_test package lockedfile_test
import ( import (
"fmt"
"internal/testenv"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
@ -172,3 +175,98 @@ func TestCanLockExistingFile(t *testing.T) {
f.Close() f.Close()
wait(t) wait(t)
} }
// TestSpuriousEDEADLK verifies that the spurious EDEADLK reported in
// https://golang.org/issue/32817 no longer occurs.
func TestSpuriousEDEADLK(t *testing.T) {
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 blocks on file B. (Spurious EDEADLK occurs here.)
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 unblocks and locks file B.
// P.2 unlocks file B.
testenv.MustHaveExec(t)
dirVar := t.Name() + "DIR"
if dir := os.Getenv(dirVar); dir != "" {
// Q.3 locks file B.
b, err := lockedfile.Edit(filepath.Join(dir, "B"))
if err != nil {
t.Fatal(err)
}
defer b.Close()
if err := ioutil.WriteFile(filepath.Join(dir, "locked"), []byte("ok"), 0666); err != nil {
t.Fatal(err)
}
// Q.3 blocks on file A.
a, err := lockedfile.Edit(filepath.Join(dir, "A"))
// Q.3 unblocks and locks file A.
if err != nil {
t.Fatal(err)
}
defer a.Close()
// Q.3 unlocks files A and B.
return
}
dir, remove := mustTempDir(t)
defer remove()
// P.1 locks file A.
a, err := lockedfile.Edit(filepath.Join(dir, "A"))
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(os.Args[0], "-test.run="+t.Name())
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", dirVar, dir))
qDone := make(chan struct{})
waitQ := mustBlock(t, "Edit A and B in subprocess", func() {
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("%v:\n%s", err, out)
}
close(qDone)
})
// Wait until process Q has either failed or locked file B.
// Otherwise, P.2 might not block on file B as intended.
locked:
for {
if _, err := os.Stat(filepath.Join(dir, "locked")); !os.IsNotExist(err) {
break locked
}
select {
case <-qDone:
break locked
case <-time.After(1 * time.Millisecond):
}
}
waitP2 := mustBlock(t, "Edit B", func() {
// P.2 blocks on file B. (Spurious EDEADLK occurs here.)
b, err := lockedfile.Edit(filepath.Join(dir, "B"))
// P.2 unblocks and locks file B.
if err != nil {
t.Error(err)
return
}
// P.2 unlocks file B.
b.Close()
})
// P.1 unlocks file A.
a.Close()
waitQ(t)
waitP2(t)
}

View File

@ -6,6 +6,7 @@ package modcmd
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -67,12 +68,10 @@ func verifyMod(mod module.Version) bool {
_, zipErr = os.Stat(zip) _, zipErr = os.Stat(zip)
} }
dir, dirErr := modfetch.DownloadDir(mod) dir, dirErr := modfetch.DownloadDir(mod)
if dirErr == nil {
_, dirErr = os.Stat(dir)
}
data, err := ioutil.ReadFile(zip + "hash") data, err := ioutil.ReadFile(zip + "hash")
if err != nil { if err != nil {
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) { if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) &&
dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
// Nothing downloaded yet. Nothing to verify. // Nothing downloaded yet. Nothing to verify.
return true return true
} }
@ -81,7 +80,7 @@ func verifyMod(mod module.Version) bool {
} }
h := string(bytes.TrimSpace(data)) h := string(bytes.TrimSpace(data))
if zipErr != nil && os.IsNotExist(zipErr) { if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) {
// ok // ok
} else { } else {
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash) hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
@ -93,7 +92,7 @@ func verifyMod(mod module.Version) bool {
ok = false ok = false
} }
} }
if dirErr != nil && os.IsNotExist(dirErr) { if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
// ok // ok
} else { } else {
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash) hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)

View File

@ -7,6 +7,7 @@ package modfetch
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -56,8 +57,11 @@ func CachePath(m module.Version, suffix string) (string, error) {
return filepath.Join(dir, encVer+"."+suffix), nil return filepath.Join(dir, encVer+"."+suffix), nil
} }
// DownloadDir returns the directory to which m should be downloaded. // DownloadDir returns the directory to which m should have been downloaded.
// Note that the directory may not yet exist. // An error will be returned if the module path or version cannot be escaped.
// An error satisfying errors.Is(err, os.ErrNotExist) will be returned
// along with the directory if the directory does not exist or if the directory
// is not completely populated.
func DownloadDir(m module.Version) (string, error) { func DownloadDir(m module.Version) (string, error) {
if PkgMod == "" { if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set") return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
@ -76,8 +80,38 @@ func DownloadDir(m module.Version) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return filepath.Join(PkgMod, enc+"@"+encVer), nil
dir := filepath.Join(PkgMod, enc+"@"+encVer)
if fi, err := os.Stat(dir); os.IsNotExist(err) {
return dir, err
} else if err != nil {
return dir, &DownloadDirPartialError{dir, err}
} else if !fi.IsDir() {
return dir, &DownloadDirPartialError{dir, errors.New("not a directory")}
} }
partialPath, err := CachePath(m, "partial")
if err != nil {
return dir, err
}
if _, err := os.Stat(partialPath); err == nil {
return dir, &DownloadDirPartialError{dir, errors.New("not completely extracted")}
} else if !os.IsNotExist(err) {
return dir, err
}
return dir, nil
}
// DownloadDirPartialError is returned by DownloadDir if a module directory
// exists but was not completely populated.
//
// DownloadDirPartialError is equivalent to os.ErrNotExist.
type DownloadDirPartialError struct {
Dir string
Err error
}
func (e *DownloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", e.Dir, e.Err) }
func (e *DownloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist }
// lockVersion locks a file within the module cache that guards the downloading // lockVersion locks a file within the module cache that guards the downloading
// and extraction of the zipfile for the given module version. // and extraction of the zipfile for the given module version.

View File

@ -563,7 +563,7 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
return err return err
} }
if !t.Equal(info.Time.Truncate(time.Second)) { if !t.Equal(info.Time.Truncate(time.Second)) {
return fmt.Errorf("does not match version-control timestamp (%s)", info.Time.UTC().Format(time.RFC3339)) return fmt.Errorf("does not match version-control timestamp (expected %s)", info.Time.UTC().Format(pseudoVersionTimestampFormat))
} }
tagPrefix := "" tagPrefix := ""

View File

@ -46,24 +46,26 @@ func Download(mod module.Version) (dir string, err error) {
err error err error
} }
c := downloadCache.Do(mod, func() interface{} { c := downloadCache.Do(mod, func() interface{} {
dir, err := DownloadDir(mod) dir, err := download(mod)
if err != nil { if err != nil {
return cached{"", err} return cached{"", err}
} }
if err := download(mod, dir); err != nil {
return cached{"", err}
}
checkMod(mod) checkMod(mod)
return cached{dir, nil} return cached{dir, nil}
}).(cached) }).(cached)
return c.dir, c.err return c.dir, c.err
} }
func download(mod module.Version, dir string) (err error) { func download(mod module.Version) (dir string, err error) {
// If the directory exists, the module has already been extracted. // If the directory exists, and no .partial file exists, the module has
fi, err := os.Stat(dir) // already been completely extracted. .partial files may be created when a
if err == nil && fi.IsDir() { // module zip directory is extracted in place instead of being extracted to a
return nil // temporary directory and renamed.
dir, err = DownloadDir(mod)
if err == nil {
return dir, nil
} else if dir == "" || !errors.Is(err, os.ErrNotExist) {
return "", err
} }
// To avoid cluttering the cache with extraneous files, // To avoid cluttering the cache with extraneous files,
@ -71,22 +73,24 @@ func download(mod module.Version, dir string) (err error) {
// Invoke DownloadZip before locking the file. // Invoke DownloadZip before locking the file.
zipfile, err := DownloadZip(mod) zipfile, err := DownloadZip(mod)
if err != nil { if err != nil {
return err return "", err
} }
unlock, err := lockVersion(mod) unlock, err := lockVersion(mod)
if err != nil { if err != nil {
return err return "", err
} }
defer unlock() defer unlock()
// Check whether the directory was populated while we were waiting on the lock. // Check whether the directory was populated while we were waiting on the lock.
fi, err = os.Stat(dir) _, dirErr := DownloadDir(mod)
if err == nil && fi.IsDir() { if dirErr == nil {
return nil return dir, nil
} }
_, dirExists := dirErr.(*DownloadDirPartialError)
// Clean up any remaining temporary directories from previous runs. // Clean up any remaining temporary directories from previous runs, as well
// as partially extracted diectories created by future versions of cmd/go.
// This is only safe to do because the lock file ensures that their writers // This is only safe to do because the lock file ensures that their writers
// are no longer active. // are no longer active.
parentDir := filepath.Dir(dir) parentDir := filepath.Dir(dir)
@ -96,35 +100,75 @@ func download(mod module.Version, dir string) (err error) {
RemoveAll(path) // best effort RemoveAll(path) // best effort
} }
} }
if dirExists {
// Extract the zip file to a temporary directory, then rename it to the if err := RemoveAll(dir); err != nil {
// final path. That way, we can use the existence of the source directory to return "", err
// signal that it has been extracted successfully, and if someone deletes
// the entire directory (e.g. as an attempt to prune out file corruption)
// the module cache will still be left in a recoverable state.
// We retry failed renames using robustio.Rename on Windows. Programs that
// open files in the temporary directory (antivirus, search indexers, etc.)
// can cause os.Rename to fail with ERROR_ACCESS_DENIED.
if err := os.MkdirAll(parentDir, 0777); err != nil {
return err
} }
}
partialPath, err := CachePath(mod, "partial")
if err != nil {
return "", err
}
if err := os.Remove(partialPath); err != nil && !os.IsNotExist(err) {
return "", err
}
// Extract the module zip directory.
//
// By default, we extract to a temporary directory, then atomically rename to
// its final location. We use the existence of the source directory to signal
// that it has been extracted successfully (see DownloadDir). If someone
// deletes the entire directory (e.g., as an attempt to prune out file
// corruption), the module cache will still be left in a recoverable
// state.
//
// Unfortunately, os.Rename may fail with ERROR_ACCESS_DENIED on Windows if
// another process opens files in the temporary directory. This is partially
// mitigated by using robustio.Rename, which retries os.Rename for a short
// time.
//
// To avoid this error completely, if unzipInPlace is set, we instead create a
// .partial file (indicating the directory isn't fully extracted), then we
// extract the directory at its final location, then we delete the .partial
// file. This is not the default behavior because older versions of Go may
// simply stat the directory to check whether it exists without looking for a
// .partial file. If multiple versions run concurrently, the older version may
// assume a partially extracted directory is complete.
// TODO(golang.org/issue/36568): when these older versions are no longer
// supported, remove the old default behavior and the unzipInPlace flag.
if err := os.MkdirAll(parentDir, 0777); err != nil {
return "", err
}
if unzipInPlace {
if err := ioutil.WriteFile(partialPath, nil, 0666); err != nil {
return "", err
}
if err := modzip.Unzip(dir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
if rmErr := RemoveAll(dir); rmErr == nil {
os.Remove(partialPath)
}
return "", err
}
if err := os.Remove(partialPath); err != nil {
return "", err
}
} else {
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix) tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
if err != nil { if err != nil {
return err return "", err
} }
defer func() {
if err != nil {
RemoveAll(tmpDir)
}
}()
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil { if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err) fmt.Fprintf(os.Stderr, "-> %s\n", err)
return err RemoveAll(tmpDir)
return "", err
} }
if err := robustio.Rename(tmpDir, dir); err != nil { if err := robustio.Rename(tmpDir, dir); err != nil {
return err RemoveAll(tmpDir)
return "", err
}
} }
if !cfg.ModCacheRW { if !cfg.ModCacheRW {
@ -132,7 +176,18 @@ func download(mod module.Version, dir string) (err error) {
// os.Rename was observed to fail for read-only directories on macOS. // os.Rename was observed to fail for read-only directories on macOS.
makeDirsReadOnly(dir) makeDirsReadOnly(dir)
} }
return nil return dir, nil
}
var unzipInPlace bool
func init() {
for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
if f == "modcacheunzipinplace=1" {
unzipInPlace = true
break
}
}
} }
var downloadZipCache par.Cache var downloadZipCache par.Cache
@ -306,7 +361,7 @@ func RemoveAll(dir string) error {
} }
return nil return nil
}) })
return os.RemoveAll(dir) return robustio.RemoveAll(dir)
} }
var GoSumFile string // path to go.sum; set by package modload var GoSumFile string // path to go.sum; set by package modload

View File

@ -48,6 +48,8 @@ import (
var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`) var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
const pseudoVersionTimestampFormat = "20060102150405"
// PseudoVersion returns a pseudo-version for the given major version ("v1") // PseudoVersion returns a pseudo-version for the given major version ("v1")
// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time, // preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
// and revision identifier (usually a 12-byte commit hash prefix). // and revision identifier (usually a 12-byte commit hash prefix).
@ -55,7 +57,7 @@ func PseudoVersion(major, older string, t time.Time, rev string) string {
if major == "" { if major == "" {
major = "v0" major = "v0"
} }
segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev) segment := fmt.Sprintf("%s-%s", t.UTC().Format(pseudoVersionTimestampFormat), rev)
build := semver.Build(older) build := semver.Build(older)
older = semver.Canonical(older) older = semver.Canonical(older)
if older == "" { if older == "" {

View File

@ -148,12 +148,10 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
} }
dir, err := modfetch.DownloadDir(mod) dir, err := modfetch.DownloadDir(mod)
if err == nil { if err == nil {
if info, err := os.Stat(dir); err == nil && info.IsDir() {
m.Dir = dir m.Dir = dir
} }
} }
} }
}
if !fromBuildList { if !fromBuildList {
completeFromModCache(info) // Will set m.Error in vendor mode. completeFromModCache(info) // Will set m.Error in vendor mode.

View File

@ -59,27 +59,6 @@ var (
allowMissingModuleImports bool allowMissingModuleImports bool
) )
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
// at a specific point in time.
type modFileIndex struct {
data []byte
dataNeedsFix bool // true if fixVersion applied a change while parsing data
module module.Version
goVersion string
require map[module.Version]requireMeta
replace map[module.Version]module.Version
exclude map[module.Version]bool
}
// index is the index of the go.mod file as of when it was last read or written.
var index *modFileIndex
type requireMeta struct {
indirect bool
}
// ModFile returns the parsed go.mod file. // ModFile returns the parsed go.mod file.
// //
// Note that after calling ImportPaths or LoadBuildList, // Note that after calling ImportPaths or LoadBuildList,
@ -555,101 +534,6 @@ func setDefaultBuildMod() {
} }
} }
// checkVendorConsistency verifies that the vendor/modules.txt file matches (if
// go 1.14) or at least does not contradict (go 1.13 or earlier) the
// requirements and replacements listed in the main module's go.mod file.
func checkVendorConsistency() {
readVendorList()
pre114 := false
if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, "v1.14") < 0 {
// Go versions before 1.14 did not include enough information in
// vendor/modules.txt to check for consistency.
// If we know that we're on an earlier version, relax the consistency check.
pre114 = true
}
vendErrors := new(strings.Builder)
vendErrorf := func(mod module.Version, format string, args ...interface{}) {
detail := fmt.Sprintf(format, args...)
if mod.Version == "" {
fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
} else {
fmt.Fprintf(vendErrors, "\n\t%s@%s: %s", mod.Path, mod.Version, detail)
}
}
for _, r := range modFile.Require {
if !vendorMeta[r.Mod].Explicit {
if pre114 {
// Before 1.14, modules.txt did not indicate whether modules were listed
// explicitly in the main module's go.mod file.
// However, we can at least detect a version mismatch if packages were
// vendored from a non-matching version.
if vv, ok := vendorVersion[r.Mod.Path]; ok && vv != r.Mod.Version {
vendErrorf(r.Mod, fmt.Sprintf("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s", r.Mod.Path, vv))
}
} else {
vendErrorf(r.Mod, "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt")
}
}
}
describe := func(m module.Version) string {
if m.Version == "" {
return m.Path
}
return m.Path + "@" + m.Version
}
// We need to verify *all* replacements that occur in modfile: even if they
// don't directly apply to any module in the vendor list, the replacement
// go.mod file can affect the selected versions of other (transitive)
// dependencies
for _, r := range modFile.Replace {
vr := vendorMeta[r.Old].Replacement
if vr == (module.Version{}) {
if pre114 && (r.Old.Version == "" || vendorVersion[r.Old.Path] != r.Old.Version) {
// Before 1.14, modules.txt omitted wildcard replacements and
// replacements for modules that did not have any packages to vendor.
} else {
vendErrorf(r.Old, "is replaced in go.mod, but not marked as replaced in vendor/modules.txt")
}
} else if vr != r.New {
vendErrorf(r.Old, "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt", describe(r.New), describe(vr))
}
}
for _, mod := range vendorList {
meta := vendorMeta[mod]
if meta.Explicit {
if _, inGoMod := index.require[mod]; !inGoMod {
vendErrorf(mod, "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod")
}
}
}
for _, mod := range vendorReplaced {
r := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue
}
if meta := vendorMeta[mod]; r != meta.Replacement {
vendErrorf(mod, "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod", describe(meta.Replacement), describe(r))
}
}
if vendErrors.Len() > 0 {
base.Fatalf("go: inconsistent vendoring in %s:%s\n\nrun 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory", modRoot, vendErrors)
}
}
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
func Allowed(m module.Version) bool {
return index == nil || !index.exclude[m]
}
func legacyModInit() { func legacyModInit() {
if modFile == nil { if modFile == nil {
path, err := findModulePath(modRoot) path, err := findModulePath(modRoot)
@ -983,113 +867,3 @@ func WriteGoMod() {
base.Fatalf("go: updating go.mod: %v", err) base.Fatalf("go: updating go.mod: %v", err)
} }
} }
// indexModFile rebuilds the index of modFile.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
i.module = module.Version{}
if modFile.Module != nil {
i.module = modFile.Module.Mod
}
i.goVersion = ""
if modFile.Go != nil {
i.goVersion = modFile.Go.Version
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
for _, r := range modFile.Require {
i.require[r.Mod] = requireMeta{indirect: r.Indirect}
}
i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
for _, r := range modFile.Replace {
if prev, dup := i.replace[r.Old]; dup && prev != r.New {
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
}
i.replace[r.Old] = r.New
}
i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
for _, x := range modFile.Exclude {
i.exclude[x.Mod] = true
}
return i
}
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
// modFile.Cleanup must be called before modFileIsDirty.
func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
if i == nil {
return modFile != nil
}
if i.dataNeedsFix {
return true
}
if modFile.Module == nil {
if i.module != (module.Version{}) {
return true
}
} else if modFile.Module.Mod != i.module {
return true
}
if modFile.Go == nil {
if i.goVersion != "" {
return true
}
} else if modFile.Go.Version != i.goVersion {
if i.goVersion == "" && cfg.BuildMod == "readonly" {
// go.mod files did not always require a 'go' version, so do not error out
// if one is missing — we may be inside an older module in the module
// cache, and should bias toward providing useful behavior.
} else {
return true
}
}
if len(modFile.Require) != len(i.require) ||
len(modFile.Replace) != len(i.replace) ||
len(modFile.Exclude) != len(i.exclude) {
return true
}
for _, r := range modFile.Require {
if meta, ok := i.require[r.Mod]; !ok {
return true
} else if r.Indirect != meta.indirect {
if cfg.BuildMod == "readonly" {
// The module's requirements are consistent; only the "// indirect"
// comments that are wrong. But those are only guaranteed to be accurate
// after a "go mod tidy" — it's a good idea to run those before
// committing a change, but it's certainly not mandatory.
} else {
return true
}
}
}
for _, r := range modFile.Replace {
if r.New != i.replace[r.Old] {
return true
}
}
for _, x := range modFile.Exclude {
if !i.exclude[x.Mod] {
return true
}
}
return false
}

View File

@ -9,14 +9,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"go/build" "go/build"
"io/ioutil"
"os" "os"
"path" "path"
pathpkg "path" pathpkg "path"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
@ -27,9 +25,7 @@ import (
"cmd/go/internal/search" "cmd/go/internal/search"
"cmd/go/internal/str" "cmd/go/internal/str"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module" "golang.org/x/mod/module"
"golang.org/x/mod/semver"
) )
// buildList is the list of modules to use for building packages. // buildList is the list of modules to use for building packages.
@ -1033,354 +1029,3 @@ func WhyDepth(path string) int {
} }
return n return n
} }
// Replacement returns the replacement for mod, if any, from go.mod.
// If there is no replacement for mod, Replacement returns
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if index != nil {
if r, ok := index.replace[mod]; ok {
return r
}
if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
return r
}
}
return module.Version{}
}
// mvsReqs implements mvs.Reqs for module semantic versions,
// with any exclusions or replacements applied internally.
type mvsReqs struct {
buildList []module.Version
cache par.Cache
versions sync.Map
}
// Reqs returns the current module requirement graph.
// Future calls to SetBuildList do not affect the operation
// of the returned Reqs.
func Reqs() mvs.Reqs {
r := &mvsReqs{
buildList: buildList,
}
return r
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
type cached struct {
list []module.Version
err error
}
c := r.cache.Do(mod, func() interface{} {
list, err := r.required(mod)
if err != nil {
return cached{nil, err}
}
for i, mv := range list {
if index != nil {
for index.exclude[mv] {
mv1, err := r.next(mv)
if err != nil {
return cached{nil, err}
}
if mv1.Version == "none" {
return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
}
mv = mv1
}
}
list[i] = mv
}
return cached{list, nil}
}).(cached)
return c.list, c.err
}
var vendorOnce sync.Once
type vendorMetadata struct {
Explicit bool
Replacement module.Version
}
var (
vendorList []module.Version // modules that contribute packages to the build, in order of appearance
vendorReplaced []module.Version // all replaced modules; may or may not also contribute packages
vendorVersion map[string]string // module path → selected version (if known)
vendorPkgModule map[string]module.Version // package → containing module
vendorMeta map[module.Version]vendorMetadata
)
// readVendorList reads the list of vendored modules from vendor/modules.txt.
func readVendorList() {
vendorOnce.Do(func() {
vendorList = nil
vendorPkgModule = make(map[string]module.Version)
vendorVersion = make(map[string]string)
vendorMeta = make(map[module.Version]vendorMetadata)
data, err := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
base.Fatalf("go: %s", err)
}
return
}
var mod module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
f := strings.Fields(line)
if len(f) < 3 {
continue
}
if semver.IsValid(f[2]) {
// A module, but we don't yet know whether it is in the build list or
// only included to indicate a replacement.
mod = module.Version{Path: f[1], Version: f[2]}
f = f[3:]
} else if f[2] == "=>" {
// A wildcard replacement found in the main module's go.mod file.
mod = module.Version{Path: f[1]}
f = f[2:]
} else {
// Not a version or a wildcard replacement.
// We don't know how to interpret this module line, so ignore it.
mod = module.Version{}
continue
}
if len(f) >= 2 && f[0] == "=>" {
meta := vendorMeta[mod]
if len(f) == 2 {
// File replacement.
meta.Replacement = module.Version{Path: f[1]}
vendorReplaced = append(vendorReplaced, mod)
} else if len(f) == 3 && semver.IsValid(f[2]) {
// Path and version replacement.
meta.Replacement = module.Version{Path: f[1], Version: f[2]}
vendorReplaced = append(vendorReplaced, mod)
} else {
// We don't understand this replacement. Ignore it.
}
vendorMeta[mod] = meta
}
continue
}
// Not a module line. Must be a package within a module or a metadata
// directive, either of which requires a preceding module line.
if mod.Path == "" {
continue
}
if strings.HasPrefix(line, "## ") {
// Metadata. Take the union of annotations across multiple lines, if present.
meta := vendorMeta[mod]
for _, entry := range strings.Split(strings.TrimPrefix(line, "## "), ";") {
entry = strings.TrimSpace(entry)
if entry == "explicit" {
meta.Explicit = true
}
// All other tokens are reserved for future use.
}
vendorMeta[mod] = meta
continue
}
if f := strings.Fields(line); len(f) == 1 && module.CheckImportPath(f[0]) == nil {
// A package within the current module.
vendorPkgModule[f[0]] = mod
// Since this module provides a package for the build, we know that it
// is in the build list and is the selected version of its path.
// If this information is new, record it.
if v, ok := vendorVersion[mod.Path]; !ok || semver.Compare(v, mod.Version) < 0 {
vendorList = append(vendorList, mod)
vendorVersion[mod.Path] = mod.Version
}
}
}
})
}
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
list := make([]module.Version, 0, len(f.Require))
for _, r := range f.Require {
list = append(list, r.Mod)
}
return list
}
// required returns a unique copy of the requirements of mod.
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
return append([]module.Version(nil), r.buildList[1:]...), nil
}
if cfg.BuildMod == "vendor" {
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
return append([]module.Version(nil), vendorList...), nil
}
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
f, err := modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
mod = repl
}
if mod.Version == "none" {
return nil, nil
}
if !semver.IsValid(mod.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
data, err := modfetch.GoMod(mod.Path, mod.Version)
if err != nil {
return nil, err
}
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(mod, fmt.Errorf("parsing go.mod: %v", err))
}
if f.Module == nil {
return nil, module.VersionError(mod, errors.New("parsing go.mod: missing module line"))
}
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, mod.Path))
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
func (*mvsReqs) Max(v1, v2 string) string {
if v1 != "" && semver.Compare(v1, v2) == -1 {
return v2
}
return v1
}
// Upgrade is a no-op, here to implement mvs.Reqs.
// The upgrade logic for go get -u is in ../modget/get.go.
func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
return m, nil
}
func versions(path string) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here.
var versions []string
err := modfetch.TryProxies(func(proxy string) error {
repo, err := modfetch.Lookup(proxy, path)
if err == nil {
versions, err = repo.Versions("")
}
return err
})
return versions, err
}
// Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
if i > 0 {
return module.Version{Path: m.Path, Version: list[i-1]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// next returns the next version of m.Path after m.Version.
// It is only used by the exclusion processing in the Required method,
// not called directly by MVS.
func (*mvsReqs) next(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
if i < len(list) {
return module.Version{Path: m.Path, Version: list[i]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// fetch downloads the given module (or its replacement)
// and returns its location.
//
// The isLocal return value reports whether the replacement,
// if any, is local to the filesystem.
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
return ModRoot(), true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
// so if we don't report the error now, later failures will be
// very mysterious.
if _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) {
// Semantically the module version itself “exists” — we just don't
// have its source code. Remove the equivalence to os.ErrNotExist,
// and make the message more concise while we're at it.
err = fmt.Errorf("replacement directory %s does not exist", r.Path)
} else {
err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
}
return dir, true, module.VersionError(mod, err)
}
return dir, true, nil
}
mod = r
}
dir, err = modfetch.Download(mod)
return dir, false, err
}

View File

@ -0,0 +1,164 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modload
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
// at a specific point in time.
type modFileIndex struct {
data []byte
dataNeedsFix bool // true if fixVersion applied a change while parsing data
module module.Version
goVersion string
require map[module.Version]requireMeta
replace map[module.Version]module.Version
exclude map[module.Version]bool
}
// index is the index of the go.mod file as of when it was last read or written.
var index *modFileIndex
type requireMeta struct {
indirect bool
}
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
func Allowed(m module.Version) bool {
return index == nil || !index.exclude[m]
}
// Replacement returns the replacement for mod, if any, from go.mod.
// If there is no replacement for mod, Replacement returns
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if index != nil {
if r, ok := index.replace[mod]; ok {
return r
}
if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
return r
}
}
return module.Version{}
}
// indexModFile rebuilds the index of modFile.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
i.module = module.Version{}
if modFile.Module != nil {
i.module = modFile.Module.Mod
}
i.goVersion = ""
if modFile.Go != nil {
i.goVersion = modFile.Go.Version
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
for _, r := range modFile.Require {
i.require[r.Mod] = requireMeta{indirect: r.Indirect}
}
i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
for _, r := range modFile.Replace {
if prev, dup := i.replace[r.Old]; dup && prev != r.New {
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
}
i.replace[r.Old] = r.New
}
i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
for _, x := range modFile.Exclude {
i.exclude[x.Mod] = true
}
return i
}
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
// modFile.Cleanup must be called before modFileIsDirty.
func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
if i == nil {
return modFile != nil
}
if i.dataNeedsFix {
return true
}
if modFile.Module == nil {
if i.module != (module.Version{}) {
return true
}
} else if modFile.Module.Mod != i.module {
return true
}
if modFile.Go == nil {
if i.goVersion != "" {
return true
}
} else if modFile.Go.Version != i.goVersion {
if i.goVersion == "" && cfg.BuildMod == "readonly" {
// go.mod files did not always require a 'go' version, so do not error out
// if one is missing — we may be inside an older module in the module
// cache, and should bias toward providing useful behavior.
} else {
return true
}
}
if len(modFile.Require) != len(i.require) ||
len(modFile.Replace) != len(i.replace) ||
len(modFile.Exclude) != len(i.exclude) {
return true
}
for _, r := range modFile.Require {
if meta, ok := i.require[r.Mod]; !ok {
return true
} else if r.Indirect != meta.indirect {
if cfg.BuildMod == "readonly" {
// The module's requirements are consistent; only the "// indirect"
// comments that are wrong. But those are only guaranteed to be accurate
// after a "go mod tidy" — it's a good idea to run those before
// committing a change, but it's certainly not mandatory.
} else {
return true
}
}
}
for _, r := range modFile.Replace {
if r.New != i.replace[r.Old] {
return true
}
}
for _, x := range modFile.Exclude {
if !i.exclude[x.Mod] {
return true
}
}
return false
}

View File

@ -0,0 +1,253 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modload
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// mvsReqs implements mvs.Reqs for module semantic versions,
// with any exclusions or replacements applied internally.
type mvsReqs struct {
buildList []module.Version
cache par.Cache
versions sync.Map
}
// Reqs returns the current module requirement graph.
// Future calls to SetBuildList do not affect the operation
// of the returned Reqs.
func Reqs() mvs.Reqs {
r := &mvsReqs{
buildList: buildList,
}
return r
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
type cached struct {
list []module.Version
err error
}
c := r.cache.Do(mod, func() interface{} {
list, err := r.required(mod)
if err != nil {
return cached{nil, err}
}
for i, mv := range list {
if index != nil {
for index.exclude[mv] {
mv1, err := r.next(mv)
if err != nil {
return cached{nil, err}
}
if mv1.Version == "none" {
return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
}
mv = mv1
}
}
list[i] = mv
}
return cached{list, nil}
}).(cached)
return c.list, c.err
}
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
list := make([]module.Version, 0, len(f.Require))
for _, r := range f.Require {
list = append(list, r.Mod)
}
return list
}
// required returns a unique copy of the requirements of mod.
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
return append([]module.Version(nil), r.buildList[1:]...), nil
}
if cfg.BuildMod == "vendor" {
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
return append([]module.Version(nil), vendorList...), nil
}
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
f, err := modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
mod = repl
}
if mod.Version == "none" {
return nil, nil
}
if !semver.IsValid(mod.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
data, err := modfetch.GoMod(mod.Path, mod.Version)
if err != nil {
return nil, err
}
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(mod, fmt.Errorf("parsing go.mod: %v", err))
}
if f.Module == nil {
return nil, module.VersionError(mod, errors.New("parsing go.mod: missing module line"))
}
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, mod.Path))
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
func (*mvsReqs) Max(v1, v2 string) string {
if v1 != "" && semver.Compare(v1, v2) == -1 {
return v2
}
return v1
}
// Upgrade is a no-op, here to implement mvs.Reqs.
// The upgrade logic for go get -u is in ../modget/get.go.
func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
return m, nil
}
func versions(path string) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here.
var versions []string
err := modfetch.TryProxies(func(proxy string) error {
repo, err := modfetch.Lookup(proxy, path)
if err == nil {
versions, err = repo.Versions("")
}
return err
})
return versions, err
}
// Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
if i > 0 {
return module.Version{Path: m.Path, Version: list[i-1]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// next returns the next version of m.Path after m.Version.
// It is only used by the exclusion processing in the Required method,
// not called directly by MVS.
func (*mvsReqs) next(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
if i < len(list) {
return module.Version{Path: m.Path, Version: list[i]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// fetch downloads the given module (or its replacement)
// and returns its location.
//
// The isLocal return value reports whether the replacement,
// if any, is local to the filesystem.
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
return ModRoot(), true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
// so if we don't report the error now, later failures will be
// very mysterious.
if _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) {
// Semantically the module version itself “exists” — we just don't
// have its source code. Remove the equivalence to os.ErrNotExist,
// and make the message more concise while we're at it.
err = fmt.Errorf("replacement directory %s does not exist", r.Path)
} else {
err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
}
return dir, true, module.VersionError(mod, err)
}
return dir, true, nil
}
mod = r
}
dir, err = modfetch.Download(mod)
return dir, false, err
}

View File

@ -0,0 +1,217 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modload
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"cmd/go/internal/base"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
var (
vendorOnce sync.Once
vendorList []module.Version // modules that contribute packages to the build, in order of appearance
vendorReplaced []module.Version // all replaced modules; may or may not also contribute packages
vendorVersion map[string]string // module path → selected version (if known)
vendorPkgModule map[string]module.Version // package → containing module
vendorMeta map[module.Version]vendorMetadata
)
type vendorMetadata struct {
Explicit bool
Replacement module.Version
}
// readVendorList reads the list of vendored modules from vendor/modules.txt.
func readVendorList() {
vendorOnce.Do(func() {
vendorList = nil
vendorPkgModule = make(map[string]module.Version)
vendorVersion = make(map[string]string)
vendorMeta = make(map[module.Version]vendorMetadata)
data, err := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
base.Fatalf("go: %s", err)
}
return
}
var mod module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
f := strings.Fields(line)
if len(f) < 3 {
continue
}
if semver.IsValid(f[2]) {
// A module, but we don't yet know whether it is in the build list or
// only included to indicate a replacement.
mod = module.Version{Path: f[1], Version: f[2]}
f = f[3:]
} else if f[2] == "=>" {
// A wildcard replacement found in the main module's go.mod file.
mod = module.Version{Path: f[1]}
f = f[2:]
} else {
// Not a version or a wildcard replacement.
// We don't know how to interpret this module line, so ignore it.
mod = module.Version{}
continue
}
if len(f) >= 2 && f[0] == "=>" {
meta := vendorMeta[mod]
if len(f) == 2 {
// File replacement.
meta.Replacement = module.Version{Path: f[1]}
vendorReplaced = append(vendorReplaced, mod)
} else if len(f) == 3 && semver.IsValid(f[2]) {
// Path and version replacement.
meta.Replacement = module.Version{Path: f[1], Version: f[2]}
vendorReplaced = append(vendorReplaced, mod)
} else {
// We don't understand this replacement. Ignore it.
}
vendorMeta[mod] = meta
}
continue
}
// Not a module line. Must be a package within a module or a metadata
// directive, either of which requires a preceding module line.
if mod.Path == "" {
continue
}
if strings.HasPrefix(line, "## ") {
// Metadata. Take the union of annotations across multiple lines, if present.
meta := vendorMeta[mod]
for _, entry := range strings.Split(strings.TrimPrefix(line, "## "), ";") {
entry = strings.TrimSpace(entry)
if entry == "explicit" {
meta.Explicit = true
}
// All other tokens are reserved for future use.
}
vendorMeta[mod] = meta
continue
}
if f := strings.Fields(line); len(f) == 1 && module.CheckImportPath(f[0]) == nil {
// A package within the current module.
vendorPkgModule[f[0]] = mod
// Since this module provides a package for the build, we know that it
// is in the build list and is the selected version of its path.
// If this information is new, record it.
if v, ok := vendorVersion[mod.Path]; !ok || semver.Compare(v, mod.Version) < 0 {
vendorList = append(vendorList, mod)
vendorVersion[mod.Path] = mod.Version
}
}
}
})
}
// checkVendorConsistency verifies that the vendor/modules.txt file matches (if
// go 1.14) or at least does not contradict (go 1.13 or earlier) the
// requirements and replacements listed in the main module's go.mod file.
func checkVendorConsistency() {
readVendorList()
pre114 := false
if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, "v1.14") < 0 {
// Go versions before 1.14 did not include enough information in
// vendor/modules.txt to check for consistency.
// If we know that we're on an earlier version, relax the consistency check.
pre114 = true
}
vendErrors := new(strings.Builder)
vendErrorf := func(mod module.Version, format string, args ...interface{}) {
detail := fmt.Sprintf(format, args...)
if mod.Version == "" {
fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
} else {
fmt.Fprintf(vendErrors, "\n\t%s@%s: %s", mod.Path, mod.Version, detail)
}
}
for _, r := range modFile.Require {
if !vendorMeta[r.Mod].Explicit {
if pre114 {
// Before 1.14, modules.txt did not indicate whether modules were listed
// explicitly in the main module's go.mod file.
// However, we can at least detect a version mismatch if packages were
// vendored from a non-matching version.
if vv, ok := vendorVersion[r.Mod.Path]; ok && vv != r.Mod.Version {
vendErrorf(r.Mod, fmt.Sprintf("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s", r.Mod.Path, vv))
}
} else {
vendErrorf(r.Mod, "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt")
}
}
}
describe := func(m module.Version) string {
if m.Version == "" {
return m.Path
}
return m.Path + "@" + m.Version
}
// We need to verify *all* replacements that occur in modfile: even if they
// don't directly apply to any module in the vendor list, the replacement
// go.mod file can affect the selected versions of other (transitive)
// dependencies
for _, r := range modFile.Replace {
vr := vendorMeta[r.Old].Replacement
if vr == (module.Version{}) {
if pre114 && (r.Old.Version == "" || vendorVersion[r.Old.Path] != r.Old.Version) {
// Before 1.14, modules.txt omitted wildcard replacements and
// replacements for modules that did not have any packages to vendor.
} else {
vendErrorf(r.Old, "is replaced in go.mod, but not marked as replaced in vendor/modules.txt")
}
} else if vr != r.New {
vendErrorf(r.Old, "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt", describe(r.New), describe(vr))
}
}
for _, mod := range vendorList {
meta := vendorMeta[mod]
if meta.Explicit {
if _, inGoMod := index.require[mod]; !inGoMod {
vendErrorf(mod, "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod")
}
}
}
for _, mod := range vendorReplaced {
r := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue
}
if meta := vendorMeta[mod]; r != meta.Replacement {
vendErrorf(mod, "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod", describe(meta.Replacement), describe(r))
}
}
if vendErrors.Len() > 0 {
base.Fatalf("go: inconsistent vendoring in %s:%s\n\nrun 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory", modRoot, vendErrors)
}
}

View File

@ -15,7 +15,7 @@ import (
"time" "time"
) )
const arbitraryTimeout = 500 * time.Millisecond const arbitraryTimeout = 2000 * time.Millisecond
// retry retries ephemeral errors from f up to an arbitrary timeout // retry retries ephemeral errors from f up to an arbitrary timeout
// to work around filesystem flakiness on Windows and Darwin. // to work around filesystem flakiness on Windows and Darwin.

View File

@ -213,6 +213,9 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
} else if cfg.BuildTrimpath && p.Module != nil { } else if cfg.BuildTrimpath && p.Module != nil {
fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version) fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)
} }
if p.Module != nil {
fmt.Fprintf(h, "go %s\n", p.Module.GoVersion)
}
fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch) fmt.Fprintf(h, "goos %s goarch %s\n", cfg.Goos, cfg.Goarch)
fmt.Fprintf(h, "import %q\n", p.ImportPath) fmt.Fprintf(h, "import %q\n", p.ImportPath)
fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix) fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)

View File

@ -109,6 +109,7 @@ func (ts *testScript) setup() {
"CCACHE_DISABLE=1", // ccache breaks with non-existent HOME "CCACHE_DISABLE=1", // ccache breaks with non-existent HOME
"GOARCH=" + runtime.GOARCH, "GOARCH=" + runtime.GOARCH,
"GOCACHE=" + testGOCACHE, "GOCACHE=" + testGOCACHE,
"GODEBUG=" + os.Getenv("GODEBUG"),
"GOEXE=" + cfg.ExeSuffix, "GOEXE=" + cfg.ExeSuffix,
"GOOS=" + runtime.GOOS, "GOOS=" + runtime.GOOS,
"GOPATH=" + filepath.Join(ts.workdir, "gopath"), "GOPATH=" + filepath.Join(ts.workdir, "gopath"),

View File

@ -36,6 +36,7 @@ Scripts also have access to these other environment variables:
HOME=/no-home HOME=/no-home
PATH=<actual PATH> PATH=<actual PATH>
TMPDIR=$WORK/tmp TMPDIR=$WORK/tmp
GODEBUG=<actual GODEBUG>
devnull=<value of os.DevNull> devnull=<value of os.DevNull>
goversion=<current Go version; for example, 1.12> goversion=<current Go version; for example, 1.12>
:=<OS-specific path list separator> :=<OS-specific path list separator>

View File

@ -7,7 +7,8 @@ env GO111MODULE=off
[!linux] skip # test only works if c-archive implies -shared [!linux] skip # test only works if c-archive implies -shared
[short] skip [short] skip
go build -x -buildmode=c-archive -gcflags=all=-shared=false ./override.go env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
go build -x -n -buildmode=c-archive -gcflags=all=-shared=false ./override.go
stderr '^.*/compile (.* )?-shared (.* )?-shared=false' stderr '^.*/compile (.* )?-shared (.* )?-shared=false'
-- override.go -- -- override.go --

View File

@ -1,11 +1,12 @@
[short] skip [short] skip
[!cgo] skip [!cgo] skip
go run -x main.go env GOCACHE=$WORK/gocache # Looking for compile flags, so need a clean cache.
go build -x -n main.go
stderr '"-I[^"]+c flags"' # find quoted c flags stderr '"-I[^"]+c flags"' # find quoted c flags
! stderr '"-I[^"]+c flags".*"-I[^"]+c flags"' # don't find too many quoted c flags ! stderr '"-I[^"]+c flags".*"-I[^"]+c flags"' # don't find too many quoted c flags per line
stderr '"-L[^"]+ld flags"' # find quoted ld flags stderr '"-L[^"]+ld flags"' # find quoted ld flags
! stderr '"-L[^"]+c flags".*"-L[^"]+c flags"' # don't find too many quoted ld flags ! stderr '"-L[^"]+c flags".*"-L[^"]+c flags"' # don't find too many quoted ld flags per line
-- main.go -- -- main.go --
package main package main

View File

@ -0,0 +1,31 @@
[short] skip
go test -cover ./coverblank
stdout 'coverage: 100.0% of statements'
-- coverblank/a.go --
package coverblank
func _() {
println("unreachable")
}
type X int
func (x X) Print() {
println(x)
}
func (x X) _() {
println("unreachable")
}
-- coverblank/a_test.go --
package coverblank
import "testing"
func TestX(t *testing.T) {
var x X
x.Print()
}

View File

@ -3,6 +3,8 @@ env GO111MODULE=off
[!gc] skip 'using -gcflags and -ldflags' [!gc] skip 'using -gcflags and -ldflags'
[short] skip [short] skip
env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
# -gcflags=-e applies to named packages, not dependencies # -gcflags=-e applies to named packages, not dependencies
go build -n -v -gcflags=-e z1 z2 go build -n -v -gcflags=-e z1 z2
stderr 'compile.* -e.* -p z1' stderr 'compile.* -e.* -p z1'

View File

@ -0,0 +1,17 @@
# This tests checks the GODEBUG=modcacheunzipinplace=1 flag, used as part of
# a migration in golang.org/issue/36568.
#
# Concurrent downloads with and without GODEBUG=modcacheunzipinplace=1 should
# not conflict. This is meant to simulate an old version and a new version
# of Go accessing the cache concurrently.
go mod download &
env GODEBUG=modcacheunzipinplace=1
go mod download
wait
-- go.mod --
module golang.org/issue/36568
go 1.14
require rsc.io/quote v1.5.2

View File

@ -0,0 +1,120 @@
# This test simulates a process watching for changes and reading files in
# module cache as a module is extracted.
#
# By default, we unzip a downloaded module into a temporary directory with a
# random name, then rename the directory into place. On Windows, this fails
# with ERROR_ACCESS_DENIED if another process (e.g., antivirus) opens files
# in the directory.
#
# Setting GODEBUG=modcacheunzipinplace=1 opts into new behavior: a downloaded
# module is unzipped in place. A .partial file is created elsewhere to indicate
# that the extraction is incomplete.
#
# Verifies golang.org/issue/36568.
[!windows] skip
[short] skip
# Control case: check that the default behavior fails.
# This is commented out to avoid flakiness. We can't reproduce the failure
# 100% of the time.
# ! go run downloader.go
# Experiment: check that the new behavior does not fail.
env GODEBUG=modcacheunzipinplace=1
go run downloader.go
-- go.mod --
module example.com/m
go 1.14
-- downloader.go --
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
// run repeatedly downloads a module while opening files in the module cache
// in a background goroutine.
//
// run uses a different temporary module cache in each iteration so that we
// don't need to clean the cache or synchronize closing files after each
// iteration.
func run() (err error) {
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
defer func() {
if rmErr := os.RemoveAll(tmpDir); err == nil && rmErr != nil {
err = rmErr
}
}()
for i := 0; i < 10; i++ {
gopath := filepath.Join(tmpDir, fmt.Sprintf("gopath%d", i))
var err error
done := make(chan struct{})
go func() {
err = download(gopath)
close(done)
}()
readCache(gopath, done)
if err != nil {
return err
}
}
return nil
}
// download downloads a module into the given cache using 'go mod download'.
func download(gopath string) error {
cmd := exec.Command("go", "mod", "download", "-modcacherw", "rsc.io/quote@v1.5.2")
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
return cmd.Run()
}
// readCache repeatedly globs for go.mod files in the given cache, then opens
// those files for reading. When the done chan is closed, readCache closes
// files and returns.
func readCache(gopath string, done <-chan struct{}) {
files := make(map[string]*os.File)
defer func() {
for _, f := range files {
f.Close()
}
}()
pattern := filepath.Join(gopath, "pkg/mod/rsc.io/quote@v1.5.2*/go.mod")
for {
select {
case <-done:
return
default:
}
names, _ := filepath.Glob(pattern)
for _, name := range names {
if files[name] != nil {
continue
}
f, _ := os.Open(name)
if f != nil {
files[name] = f
}
}
}
}

View File

@ -0,0 +1,64 @@
# Download a module
go mod download -modcacherw rsc.io/quote
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
# 'go mod verify' should fail if we delete a file.
go mod verify
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
! go mod verify
# Create a .partial file to simulate an failure extracting the zip file.
cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
# 'go mod verify' should not fail, since the module hasn't been completely
# ingested into the cache.
go mod verify
# 'go list' should not load packages from the directory.
# NOTE: the message "directory $dir outside available modules" is reported
# for directories not in the main module, active modules in the module cache,
# or local replacements. In this case, the directory is in the right place,
# but it's incomplete, so 'go list' acts as if it's not an active module.
! go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
stderr 'outside available modules'
# 'go list -m' should not print the directory.
go list -m -f '{{.Dir}}' rsc.io/quote
! stdout .
# 'go mod download' should re-extract the module and remove the .partial file.
go mod download -modcacherw rsc.io/quote
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
# 'go list' should succeed.
go list $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
stdout '^rsc.io/quote$'
# 'go list -m' should print the directory.
go list -m -f '{{.Dir}}' rsc.io/quote
stdout 'pkg[/\\]mod[/\\]rsc.io[/\\]quote@v1.5.2'
# go mod verify should fail if we delete a file.
go mod verify
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
! go mod verify
# 'go mod download' should not leave behind a directory or a .partial file
# if there is an error extracting the zip file.
env GODEBUG=modcacheunzipinplace=1
rm $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
cp empty $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip
! go mod download
stderr 'not a valid zip file'
! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2
! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.partial
-- go.mod --
module m
go 1.14
require rsc.io/quote v1.5.2
-- empty --

View File

@ -7,6 +7,13 @@ go mod edit -go=1.9
grep 'go 1.9' go.mod grep 'go 1.9' go.mod
go build go build
# Reverting the version should force a rebuild and error instead of using
# the cached 1.9 build. (https://golang.org/issue/37804)
go mod edit -go=1.8
! go build
stderr 'type aliases only supported as of'
-- go.mod -- -- go.mod --
module m module m
go 1.8 go 1.8

View File

@ -14,6 +14,9 @@ go get -d golang.org/x/text@14c0d48
# dropping -d, we should see a build. # dropping -d, we should see a build.
[short] skip [short] skip
env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
go get -x golang.org/x/text/language@14c0d48 go get -x golang.org/x/text/language@14c0d48
stderr 'compile|cp|gccgo .*language\.a$' stderr 'compile|cp|gccgo .*language\.a$'

View File

@ -14,7 +14,8 @@ stdout 'rsc.io/quote v1.5.2'
[short] skip [short] skip
# Packages that are only imported in excluded files should not be built. # Packages that are only imported in excluded files should not be built.
go get -x . env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
go get -n -x .
stderr 'compile.* -p m ' stderr 'compile.* -p m '
! stderr 'compile.* -p example.com/version ' ! stderr 'compile.* -p example.com/version '
! stderr 'compile.* -p rsc.io/quote ' ! stderr 'compile.* -p rsc.io/quote '

View File

@ -76,17 +76,17 @@ cp go.mod.orig go.mod
go mod edit -require golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c go mod edit -require golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c
cd outside cd outside
! go list -m golang.org/x/text ! go list -m golang.org/x/text
stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(2017-09-15T03:28:32Z\)' stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
cd .. cd ..
! go list -m golang.org/x/text ! go list -m golang.org/x/text
stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(2017-09-15T03:28:32Z\)' stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
# A 'replace' directive in the main module can replace an invalid timestamp # A 'replace' directive in the main module can replace an invalid timestamp
# with a valid one. # with a valid one.
go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c
cd outside cd outside
! go list -m golang.org/x/text ! go list -m golang.org/x/text
stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(2017-09-15T03:28:32Z\)' stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-version: does not match version-control timestamp \(expected 20170915032832\)'
cd .. cd ..
go list -m golang.org/x/text go list -m golang.org/x/text
stdout 'golang.org/x/text v0.1.1-0.20190915032832-14c0d48ead0c => golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' stdout 'golang.org/x/text v0.1.1-0.20190915032832-14c0d48ead0c => golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c'

View File

@ -47,6 +47,7 @@ func findGorootModules(t *testing.T) []gorootModule {
// Use 'go list' to describe the module contained in this directory (but // Use 'go list' to describe the module contained in this directory (but
// not its dependencies). // not its dependencies).
cmd := exec.Command(goBin, "list", "-json", "-m") cmd := exec.Command(goBin, "list", "-json", "-m")
cmd.Env = append(os.Environ(), "GO111MODULE=on")
cmd.Dir = dir cmd.Dir = dir
cmd.Stderr = new(strings.Builder) cmd.Stderr = new(strings.Builder)
out, err := cmd.Output() out, err := cmd.Output()
@ -103,6 +104,7 @@ func TestAllDependenciesVendored(t *testing.T) {
// dependencies are vendored. If any imported package is missing, // dependencies are vendored. If any imported package is missing,
// 'go list -deps' will fail when attempting to load it. // 'go list -deps' will fail when attempting to load it.
cmd := exec.Command(goBin, "list", "-mod=vendor", "-deps", "./...") cmd := exec.Command(goBin, "list", "-mod=vendor", "-deps", "./...")
cmd.Env = append(os.Environ(), "GO111MODULE=on")
cmd.Dir = m.Dir cmd.Dir = m.Dir
cmd.Stderr = new(strings.Builder) cmd.Stderr = new(strings.Builder)
_, err := cmd.Output() _, err := cmd.Output()
@ -115,7 +117,8 @@ func TestAllDependenciesVendored(t *testing.T) {
// There is no vendor directory, so the module must have no dependencies. // There is no vendor directory, so the module must have no dependencies.
// Check that the list of active modules contains only the main module. // Check that the list of active modules contains only the main module.
cmd := exec.Command(goBin, "list", "-m", "all") cmd := exec.Command(goBin, "list", "-mod=mod", "-m", "all")
cmd.Env = append(os.Environ(), "GO111MODULE=on")
cmd.Dir = m.Dir cmd.Dir = m.Dir
cmd.Stderr = new(strings.Builder) cmd.Stderr = new(strings.Builder)
out, err := cmd.Output() out, err := cmd.Output()

View File

@ -944,22 +944,16 @@ const (
ASTXVW4X ASTXVW4X
ASTXVH8X ASTXVH8X
ASTXVB16X ASTXVB16X
ALXS
ALXSDX ALXSDX
ASTXS
ASTXSDX ASTXSDX
ALXSI
ALXSIWAX ALXSIWAX
ALXSIWZX ALXSIWZX
ASTXSI
ASTXSIWX ASTXSIWX
AMFVSR
AMFVSRD AMFVSRD
AMFFPRD AMFFPRD
AMFVRD AMFVRD
AMFVSRWZ AMFVSRWZ
AMFVSRLD AMFVSRLD
AMTVSR
AMTVSRD AMTVSRD
AMTFPRD AMTFPRD
AMTVRD AMTVRD
@ -968,7 +962,6 @@ const (
AMTVSRDD AMTVSRDD
AMTVSRWS AMTVSRWS
AXXLAND AXXLAND
AXXLANDQ
AXXLANDC AXXLANDC
AXXLEQV AXXLEQV
AXXLNAND AXXLNAND
@ -978,34 +971,27 @@ const (
AXXLORQ AXXLORQ
AXXLXOR AXXLXOR
AXXSEL AXXSEL
AXXMRG
AXXMRGHW AXXMRGHW
AXXMRGLW AXXMRGLW
AXXSPLT AXXSPLT
AXXSPLTW AXXSPLTW
AXXPERM AXXPERM
AXXPERMDI AXXPERMDI
AXXSI
AXXSLDWI AXXSLDWI
AXSCV
AXSCVDPSP AXSCVDPSP
AXSCVSPDP AXSCVSPDP
AXSCVDPSPN AXSCVDPSPN
AXSCVSPDPN AXSCVSPDPN
AXVCV
AXVCVDPSP AXVCVDPSP
AXVCVSPDP AXVCVSPDP
AXSCVX
AXSCVDPSXDS AXSCVDPSXDS
AXSCVDPSXWS AXSCVDPSXWS
AXSCVDPUXDS AXSCVDPUXDS
AXSCVDPUXWS AXSCVDPUXWS
AXSCVXP
AXSCVSXDDP AXSCVSXDDP
AXSCVUXDDP AXSCVUXDDP
AXSCVSXDSP AXSCVSXDSP
AXSCVUXDSP AXSCVUXDSP
AXVCVX
AXVCVDPSXDS AXVCVDPSXDS
AXVCVDPSXWS AXVCVDPSXWS
AXVCVDPUXDS AXVCVDPUXDS
@ -1014,7 +1000,6 @@ const (
AXVCVSPSXWS AXVCVSPSXWS
AXVCVSPUXDS AXVCVSPUXDS
AXVCVSPUXWS AXVCVSPUXWS
AXVCVXP
AXVCVSXDDP AXVCVSXDDP
AXVCVSXWDP AXVCVSXWDP
AXVCVUXDDP AXVCVUXDDP

View File

@ -532,22 +532,16 @@ var Anames = []string{
"STXVW4X", "STXVW4X",
"STXVH8X", "STXVH8X",
"STXVB16X", "STXVB16X",
"LXS",
"LXSDX", "LXSDX",
"STXS",
"STXSDX", "STXSDX",
"LXSI",
"LXSIWAX", "LXSIWAX",
"LXSIWZX", "LXSIWZX",
"STXSI",
"STXSIWX", "STXSIWX",
"MFVSR",
"MFVSRD", "MFVSRD",
"MFFPRD", "MFFPRD",
"MFVRD", "MFVRD",
"MFVSRWZ", "MFVSRWZ",
"MFVSRLD", "MFVSRLD",
"MTVSR",
"MTVSRD", "MTVSRD",
"MTFPRD", "MTFPRD",
"MTVRD", "MTVRD",
@ -556,7 +550,6 @@ var Anames = []string{
"MTVSRDD", "MTVSRDD",
"MTVSRWS", "MTVSRWS",
"XXLAND", "XXLAND",
"XXLANDQ",
"XXLANDC", "XXLANDC",
"XXLEQV", "XXLEQV",
"XXLNAND", "XXLNAND",
@ -566,34 +559,27 @@ var Anames = []string{
"XXLORQ", "XXLORQ",
"XXLXOR", "XXLXOR",
"XXSEL", "XXSEL",
"XXMRG",
"XXMRGHW", "XXMRGHW",
"XXMRGLW", "XXMRGLW",
"XXSPLT", "XXSPLT",
"XXSPLTW", "XXSPLTW",
"XXPERM", "XXPERM",
"XXPERMDI", "XXPERMDI",
"XXSI",
"XXSLDWI", "XXSLDWI",
"XSCV",
"XSCVDPSP", "XSCVDPSP",
"XSCVSPDP", "XSCVSPDP",
"XSCVDPSPN", "XSCVDPSPN",
"XSCVSPDPN", "XSCVSPDPN",
"XVCV",
"XVCVDPSP", "XVCVDPSP",
"XVCVSPDP", "XVCVSPDP",
"XSCVX",
"XSCVDPSXDS", "XSCVDPSXDS",
"XSCVDPSXWS", "XSCVDPSXWS",
"XSCVDPUXDS", "XSCVDPUXDS",
"XSCVDPUXWS", "XSCVDPUXWS",
"XSCVXP",
"XSCVSXDDP", "XSCVSXDDP",
"XSCVUXDDP", "XSCVUXDDP",
"XSCVSXDSP", "XSCVSXDSP",
"XSCVUXDSP", "XSCVUXDSP",
"XVCVX",
"XVCVDPSXDS", "XVCVDPSXDS",
"XVCVDPSXWS", "XVCVDPSXWS",
"XVCVDPUXDS", "XVCVDPUXDS",
@ -602,7 +588,6 @@ var Anames = []string{
"XVCVSPSXWS", "XVCVSPSXWS",
"XVCVSPUXDS", "XVCVSPUXDS",
"XVCVSPUXWS", "XVCVSPUXWS",
"XVCVXP",
"XVCVSXDDP", "XVCVSXDDP",
"XVCVSXWDP", "XVCVSXWDP",
"XVCVUXDDP", "XVCVUXDDP",

View File

@ -462,10 +462,10 @@ var optab = []Optab{
{AVSEL, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0}, /* vector select, va-form */ {AVSEL, C_VREG, C_VREG, C_VREG, C_VREG, 83, 4, 0}, /* vector select, va-form */
/* Vector splat */ /* Vector splat */
{AVSPLT, C_SCON, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector splat, vx-form */ {AVSPLTB, C_SCON, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector splat, vx-form */
{AVSPLT, C_ADDCON, C_VREG, C_NONE, C_VREG, 82, 4, 0}, {AVSPLTB, C_ADDCON, C_VREG, C_NONE, C_VREG, 82, 4, 0},
{AVSPLTI, C_SCON, C_NONE, C_NONE, C_VREG, 82, 4, 0}, /* vector splat immediate, vx-form */ {AVSPLTISB, C_SCON, C_NONE, C_NONE, C_VREG, 82, 4, 0}, /* vector splat immediate, vx-form */
{AVSPLTI, C_ADDCON, C_NONE, C_NONE, C_VREG, 82, 4, 0}, {AVSPLTISB, C_ADDCON, C_NONE, C_NONE, C_VREG, 82, 4, 0},
/* Vector AES */ /* Vector AES */
{AVCIPH, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector AES cipher, vx-form */ {AVCIPH, C_VREG, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector AES cipher, vx-form */
@ -484,27 +484,27 @@ var optab = []Optab{
{ASTXV, C_VSREG, C_NONE, C_NONE, C_SOREG, 97, 4, 0}, /* vsx vector store, dq-form */ {ASTXV, C_VSREG, C_NONE, C_NONE, C_SOREG, 97, 4, 0}, /* vsx vector store, dq-form */
/* VSX scalar load */ /* VSX scalar load */
{ALXS, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar load, xx1-form */ {ALXSDX, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar load, xx1-form */
/* VSX scalar store */ /* VSX scalar store */
{ASTXS, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar store, xx1-form */ {ASTXSDX, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar store, xx1-form */
/* VSX scalar as integer load */ /* VSX scalar as integer load */
{ALXSI, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar as integer load, xx1-form */ {ALXSIWAX, C_SOREG, C_NONE, C_NONE, C_VSREG, 87, 4, 0}, /* vsx scalar as integer load, xx1-form */
/* VSX scalar store as integer */ /* VSX scalar store as integer */
{ASTXSI, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar as integer store, xx1-form */ {ASTXSIWX, C_VSREG, C_NONE, C_NONE, C_SOREG, 86, 4, 0}, /* vsx scalar as integer store, xx1-form */
/* VSX move from VSR */ /* VSX move from VSR */
{AMFVSR, C_VSREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, /* vsx move from vsr, xx1-form */ {AMFVSRD, C_VSREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, /* vsx move from vsr, xx1-form */
{AMFVSR, C_FREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, {AMFVSRD, C_FREG, C_NONE, C_NONE, C_REG, 88, 4, 0},
{AMFVSR, C_VREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, {AMFVSRD, C_VREG, C_NONE, C_NONE, C_REG, 88, 4, 0},
/* VSX move to VSR */ /* VSX move to VSR */
{AMTVSR, C_REG, C_NONE, C_NONE, C_VSREG, 88, 4, 0}, /* vsx move to vsr, xx1-form */ {AMTVSRD, C_REG, C_NONE, C_NONE, C_VSREG, 88, 4, 0}, /* vsx move to vsr, xx1-form */
{AMTVSR, C_REG, C_REG, C_NONE, C_VSREG, 88, 4, 0}, {AMTVSRD, C_REG, C_REG, C_NONE, C_VSREG, 88, 4, 0},
{AMTVSR, C_REG, C_NONE, C_NONE, C_FREG, 88, 4, 0}, {AMTVSRD, C_REG, C_NONE, C_NONE, C_FREG, 88, 4, 0},
{AMTVSR, C_REG, C_NONE, C_NONE, C_VREG, 88, 4, 0}, {AMTVSRD, C_REG, C_NONE, C_NONE, C_VREG, 88, 4, 0},
/* VSX logical */ /* VSX logical */
{AXXLAND, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx and, xx3-form */ {AXXLAND, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx and, xx3-form */
@ -514,34 +514,34 @@ var optab = []Optab{
{AXXSEL, C_VSREG, C_VSREG, C_VSREG, C_VSREG, 91, 4, 0}, /* vsx select, xx4-form */ {AXXSEL, C_VSREG, C_VSREG, C_VSREG, C_VSREG, 91, 4, 0}, /* vsx select, xx4-form */
/* VSX merge */ /* VSX merge */
{AXXMRG, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx merge, xx3-form */ {AXXMRGHW, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx merge, xx3-form */
/* VSX splat */ /* VSX splat */
{AXXSPLT, C_VSREG, C_NONE, C_SCON, C_VSREG, 89, 4, 0}, /* vsx splat, xx2-form */ {AXXSPLTW, C_VSREG, C_NONE, C_SCON, C_VSREG, 89, 4, 0}, /* vsx splat, xx2-form */
/* VSX permute */ /* VSX permute */
{AXXPERM, C_VSREG, C_VSREG, C_SCON, C_VSREG, 90, 4, 0}, /* vsx permute, xx3-form */ {AXXPERM, C_VSREG, C_VSREG, C_NONE, C_VSREG, 90, 4, 0}, /* vsx permute, xx3-form */
/* VSX shift */ /* VSX shift */
{AXXSI, C_VSREG, C_VSREG, C_SCON, C_VSREG, 90, 4, 0}, /* vsx shift immediate, xx3-form */ {AXXSLDWI, C_VSREG, C_VSREG, C_SCON, C_VSREG, 90, 4, 0}, /* vsx shift immediate, xx3-form */
/* VSX scalar FP-FP conversion */ /* VSX scalar FP-FP conversion */
{AXSCV, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-fp conversion, xx2-form */ {AXSCVDPSP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-fp conversion, xx2-form */
/* VSX vector FP-FP conversion */ /* VSX vector FP-FP conversion */
{AXVCV, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-fp conversion, xx2-form */ {AXVCVDPSP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-fp conversion, xx2-form */
/* VSX scalar FP-integer conversion */ /* VSX scalar FP-integer conversion */
{AXSCVX, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-integer conversion, xx2-form */ {AXSCVDPSXDS, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar fp-integer conversion, xx2-form */
/* VSX scalar integer-FP conversion */ /* VSX scalar integer-FP conversion */
{AXSCVXP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar integer-fp conversion, xx2-form */ {AXSCVSXDDP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx scalar integer-fp conversion, xx2-form */
/* VSX vector FP-integer conversion */ /* VSX vector FP-integer conversion */
{AXVCVX, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-integer conversion, xx2-form */ {AXVCVDPSXDS, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector fp-integer conversion, xx2-form */
/* VSX vector integer-FP conversion */ /* VSX vector integer-FP conversion */
{AXVCVXP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector integer-fp conversion, xx2-form */ {AXVCVSXDDP, C_VSREG, C_NONE, C_NONE, C_VSREG, 89, 4, 0}, /* vsx vector integer-fp conversion, xx2-form */
/* 64-bit special registers */ /* 64-bit special registers */
{AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 66, 4, 0}, {AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 66, 4, 0},
@ -1519,13 +1519,11 @@ func buildop(ctxt *obj.Link) {
case AVSEL: /* vsel */ case AVSEL: /* vsel */
opset(AVSEL, r0) opset(AVSEL, r0)
case AVSPLT: /* vspltb, vsplth, vspltw */ case AVSPLTB: /* vspltb, vsplth, vspltw */
opset(AVSPLTB, r0)
opset(AVSPLTH, r0) opset(AVSPLTH, r0)
opset(AVSPLTW, r0) opset(AVSPLTW, r0)
case AVSPLTI: /* vspltisb, vspltish, vspltisw */ case AVSPLTISB: /* vspltisb, vspltish, vspltisw */
opset(AVSPLTISB, r0)
opset(AVSPLTISH, r0) opset(AVSPLTISH, r0)
opset(AVSPLTISW, r0) opset(AVSPLTISW, r0)
@ -1561,28 +1559,25 @@ func buildop(ctxt *obj.Link) {
case ASTXV: /* stxv */ case ASTXV: /* stxv */
opset(ASTXV, r0) opset(ASTXV, r0)
case ALXS: /* lxsdx */ case ALXSDX: /* lxsdx */
opset(ALXSDX, r0) opset(ALXSDX, r0)
case ASTXS: /* stxsdx */ case ASTXSDX: /* stxsdx */
opset(ASTXSDX, r0) opset(ASTXSDX, r0)
case ALXSI: /* lxsiwax, lxsiwzx */ case ALXSIWAX: /* lxsiwax, lxsiwzx */
opset(ALXSIWAX, r0)
opset(ALXSIWZX, r0) opset(ALXSIWZX, r0)
case ASTXSI: /* stxsiwx */ case ASTXSIWX: /* stxsiwx */
opset(ASTXSIWX, r0) opset(ASTXSIWX, r0)
case AMFVSR: /* mfvsrd, mfvsrwz (and extended mnemonics), mfvsrld */ case AMFVSRD: /* mfvsrd, mfvsrwz (and extended mnemonics), mfvsrld */
opset(AMFVSRD, r0)
opset(AMFFPRD, r0) opset(AMFFPRD, r0)
opset(AMFVRD, r0) opset(AMFVRD, r0)
opset(AMFVSRWZ, r0) opset(AMFVSRWZ, r0)
opset(AMFVSRLD, r0) opset(AMFVSRLD, r0)
case AMTVSR: /* mtvsrd, mtvsrwa, mtvsrwz (and extended mnemonics), mtvsrdd, mtvsrws */ case AMTVSRD: /* mtvsrd, mtvsrwa, mtvsrwz (and extended mnemonics), mtvsrdd, mtvsrws */
opset(AMTVSRD, r0)
opset(AMTFPRD, r0) opset(AMTFPRD, r0)
opset(AMTVRD, r0) opset(AMTVRD, r0)
opset(AMTVSRWA, r0) opset(AMTVSRWA, r0)
@ -1591,7 +1586,6 @@ func buildop(ctxt *obj.Link) {
opset(AMTVSRWS, r0) opset(AMTVSRWS, r0)
case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */ case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */
opset(AXXLANDQ, r0)
opset(AXXLANDC, r0) opset(AXXLANDC, r0)
opset(AXXLEQV, r0) opset(AXXLEQV, r0)
opset(AXXLNAND, r0) opset(AXXLNAND, r0)
@ -1605,42 +1599,38 @@ func buildop(ctxt *obj.Link) {
case AXXSEL: /* xxsel */ case AXXSEL: /* xxsel */
opset(AXXSEL, r0) opset(AXXSEL, r0)
case AXXMRG: /* xxmrghw, xxmrglw */ case AXXMRGHW: /* xxmrghw, xxmrglw */
opset(AXXMRGHW, r0)
opset(AXXMRGLW, r0) opset(AXXMRGLW, r0)
case AXXSPLT: /* xxspltw */ case AXXSPLTW: /* xxspltw */
opset(AXXSPLTW, r0) opset(AXXSPLTW, r0)
case AXXPERM: /* xxpermdi */ case AXXPERM: /* xxpermdi */
opset(AXXPERMDI, r0) opset(AXXPERM, r0)
case AXXSI: /* xxsldwi */ case AXXSLDWI: /* xxsldwi */
opset(AXXPERMDI, r0)
opset(AXXSLDWI, r0) opset(AXXSLDWI, r0)
case AXSCV: /* xscvdpsp, xscvspdp, xscvdpspn, xscvspdpn */ case AXSCVDPSP: /* xscvdpsp, xscvspdp, xscvdpspn, xscvspdpn */
opset(AXSCVDPSP, r0)
opset(AXSCVSPDP, r0) opset(AXSCVSPDP, r0)
opset(AXSCVDPSPN, r0) opset(AXSCVDPSPN, r0)
opset(AXSCVSPDPN, r0) opset(AXSCVSPDPN, r0)
case AXVCV: /* xvcvdpsp, xvcvspdp */ case AXVCVDPSP: /* xvcvdpsp, xvcvspdp */
opset(AXVCVDPSP, r0)
opset(AXVCVSPDP, r0) opset(AXVCVSPDP, r0)
case AXSCVX: /* xscvdpsxds, xscvdpsxws, xscvdpuxds, xscvdpuxws */ case AXSCVDPSXDS: /* xscvdpsxds, xscvdpsxws, xscvdpuxds, xscvdpuxws */
opset(AXSCVDPSXDS, r0)
opset(AXSCVDPSXWS, r0) opset(AXSCVDPSXWS, r0)
opset(AXSCVDPUXDS, r0) opset(AXSCVDPUXDS, r0)
opset(AXSCVDPUXWS, r0) opset(AXSCVDPUXWS, r0)
case AXSCVXP: /* xscvsxddp, xscvuxddp, xscvsxdsp, xscvuxdsp */ case AXSCVSXDDP: /* xscvsxddp, xscvuxddp, xscvsxdsp, xscvuxdsp */
opset(AXSCVSXDDP, r0)
opset(AXSCVUXDDP, r0) opset(AXSCVUXDDP, r0)
opset(AXSCVSXDSP, r0) opset(AXSCVSXDSP, r0)
opset(AXSCVUXDSP, r0) opset(AXSCVUXDSP, r0)
case AXVCVX: /* xvcvdpsxds, xvcvdpsxws, xvcvdpuxds, xvcvdpuxws, xvcvspsxds, xvcvspsxws, xvcvspuxds, xvcvspuxws */ case AXVCVDPSXDS: /* xvcvdpsxds, xvcvdpsxws, xvcvdpuxds, xvcvdpuxws, xvcvspsxds, xvcvspsxws, xvcvspuxds, xvcvspuxws */
opset(AXVCVDPSXDS, r0) opset(AXVCVDPSXDS, r0)
opset(AXVCVDPSXWS, r0) opset(AXVCVDPSXWS, r0)
opset(AXVCVDPUXDS, r0) opset(AXVCVDPUXDS, r0)
@ -1650,8 +1640,7 @@ func buildop(ctxt *obj.Link) {
opset(AXVCVSPUXDS, r0) opset(AXVCVSPUXDS, r0)
opset(AXVCVSPUXWS, r0) opset(AXVCVSPUXWS, r0)
case AXVCVXP: /* xvcvsxddp, xvcvsxwdp, xvcvuxddp, xvcvuxwdp, xvcvsxdsp, xvcvsxwsp, xvcvuxdsp, xvcvuxwsp */ case AXVCVSXDDP: /* xvcvsxddp, xvcvsxwdp, xvcvuxddp, xvcvuxwdp, xvcvsxdsp, xvcvsxwsp, xvcvuxdsp, xvcvuxwsp */
opset(AXVCVSXDDP, r0)
opset(AXVCVSXWDP, r0) opset(AXVCVSXWDP, r0)
opset(AXVCVUXDDP, r0) opset(AXVCVUXDDP, r0)
opset(AXVCVUXWDP, r0) opset(AXVCVUXWDP, r0)
@ -4616,7 +4605,7 @@ func (c *ctxt9) oprrr(a obj.As) uint32 {
case AMTVSRWS: case AMTVSRWS:
return OPVXX1(31, 403, 0) /* mtvsrws - v3.00 */ return OPVXX1(31, 403, 0) /* mtvsrws - v3.00 */
case AXXLANDQ: case AXXLAND:
return OPVXX3(60, 130, 0) /* xxland - v2.06 */ return OPVXX3(60, 130, 0) /* xxland - v2.06 */
case AXXLANDC: case AXXLANDC:
return OPVXX3(60, 138, 0) /* xxlandc - v2.06 */ return OPVXX3(60, 138, 0) /* xxlandc - v2.06 */
@ -4645,6 +4634,8 @@ func (c *ctxt9) oprrr(a obj.As) uint32 {
case AXXSPLTW: case AXXSPLTW:
return OPVXX2(60, 164, 0) /* xxspltw - v2.06 */ return OPVXX2(60, 164, 0) /* xxspltw - v2.06 */
case AXXPERM:
return OPVXX3(60, 26, 0) /* xxperm - v2.06 */
case AXXPERMDI: case AXXPERMDI:
return OPVXX3(60, 10, 0) /* xxpermdi - v2.06 */ return OPVXX3(60, 10, 0) /* xxpermdi - v2.06 */

View File

@ -175,6 +175,11 @@ func (fc *FileCache) Line(filename string, line int) ([]byte, error) {
fc.files.MoveToFront(e) fc.files.MoveToFront(e)
} }
// because //line directives can be out-of-range. (#36683)
if line-1 >= len(cf.Lines) || line-1 < 0 {
return nil, nil
}
return cf.Lines[line-1], nil return cf.Lines[line-1], nil
} }

View File

@ -1239,6 +1239,7 @@ func TestPackageNameAttr(t *testing.T) {
} }
rdr := d.Reader() rdr := d.Reader()
runtimeUnitSeen := false
for { for {
e, err := rdr.Next() e, err := rdr.Next()
if err != nil { if err != nil {
@ -1254,11 +1255,25 @@ func TestPackageNameAttr(t *testing.T) {
continue continue
} }
_, ok := e.Val(dwarfAttrGoPackageName).(string) pn, ok := e.Val(dwarfAttrGoPackageName).(string)
if !ok { if !ok {
name, _ := e.Val(dwarf.AttrName).(string) name, _ := e.Val(dwarf.AttrName).(string)
t.Errorf("found compile unit without package name: %s", name) t.Errorf("found compile unit without package name: %s", name)
} }
if pn == "" {
name, _ := e.Val(dwarf.AttrName).(string)
t.Errorf("found compile unit with empty package name: %s", name)
} else {
if pn == "runtime" {
runtimeUnitSeen = true
}
}
}
// Something is wrong if there's no runtime compilation unit.
if !runtimeUnitSeen {
t.Errorf("no package name for runtime unit")
} }
} }

View File

@ -58,16 +58,6 @@ var (
) )
const ( const (
IMAGE_FILE_MACHINE_I386 = 0x14c
IMAGE_FILE_MACHINE_AMD64 = 0x8664
IMAGE_FILE_MACHINE_ARM = 0x1c0
IMAGE_FILE_MACHINE_ARMNT = 0x1c4
IMAGE_FILE_RELOCS_STRIPPED = 0x0001
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020
IMAGE_FILE_32BIT_MACHINE = 0x0100
IMAGE_FILE_DEBUG_STRIPPED = 0x0200
IMAGE_SCN_CNT_CODE = 0x00000020 IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
@ -77,28 +67,6 @@ const (
IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
IMAGE_SCN_ALIGN_32BYTES = 0x600000 IMAGE_SCN_ALIGN_32BYTES = 0x600000
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
IMAGE_DIRECTORY_ENTRY_SECURITY = 4
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
IMAGE_DIRECTORY_ENTRY_DEBUG = 6
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
IMAGE_DIRECTORY_ENTRY_TLS = 9
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
IMAGE_DIRECTORY_ENTRY_IAT = 12
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
) )
// TODO(crawshaw): add these constants to debug/pe. // TODO(crawshaw): add these constants to debug/pe.
@ -762,11 +730,11 @@ func (f *peFile) writeFileHeader(ctxt *Link) {
default: default:
Exitf("unknown PE architecture: %v", ctxt.Arch.Family) Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
case sys.AMD64: case sys.AMD64:
fh.Machine = IMAGE_FILE_MACHINE_AMD64 fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64
case sys.I386: case sys.I386:
fh.Machine = IMAGE_FILE_MACHINE_I386 fh.Machine = pe.IMAGE_FILE_MACHINE_I386
case sys.ARM: case sys.ARM:
fh.Machine = IMAGE_FILE_MACHINE_ARMNT fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT
} }
fh.NumberOfSections = uint16(len(f.sections)) fh.NumberOfSections = uint16(len(f.sections))
@ -776,24 +744,24 @@ func (f *peFile) writeFileHeader(ctxt *Link) {
fh.TimeDateStamp = 0 fh.TimeDateStamp = 0
if ctxt.LinkMode == LinkExternal { if ctxt.LinkMode == LinkExternal {
fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED
} else { } else {
fh.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED
switch ctxt.Arch.Family { switch ctxt.Arch.Family {
case sys.AMD64, sys.I386: case sys.AMD64, sys.I386:
if ctxt.BuildMode != BuildModePIE { if ctxt.BuildMode != BuildModePIE {
fh.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED
} }
} }
} }
if pe64 != 0 { if pe64 != 0 {
var oh64 pe.OptionalHeader64 var oh64 pe.OptionalHeader64
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE
} else { } else {
var oh pe.OptionalHeader32 var oh pe.OptionalHeader32
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE
} }
fh.PointerToSymbolTable = uint32(f.symtabOffset) fh.PointerToSymbolTable = uint32(f.symtabOffset)
@ -854,36 +822,36 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) {
oh64.SizeOfHeaders = uint32(PEFILEHEADR) oh64.SizeOfHeaders = uint32(PEFILEHEADR)
oh.SizeOfHeaders = uint32(PEFILEHEADR) oh.SizeOfHeaders = uint32(PEFILEHEADR)
if windowsgui { if windowsgui {
oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
} else { } else {
oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
} }
// Mark as having awareness of terminal services, to avoid ancient compatibility hacks. // Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
// Enable DEP // Enable DEP
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
// The DLL can be relocated at load time. // The DLL can be relocated at load time.
switch ctxt.Arch.Family { switch ctxt.Arch.Family {
case sys.AMD64, sys.I386: case sys.AMD64, sys.I386:
if ctxt.BuildMode == BuildModePIE { if ctxt.BuildMode == BuildModePIE {
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
} }
case sys.ARM: case sys.ARM:
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
} }
// Image can handle a high entropy 64-bit virtual address space. // Image can handle a high entropy 64-bit virtual address space.
if ctxt.BuildMode == BuildModePIE { if ctxt.BuildMode == BuildModePIE {
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
} }
// Disable stack growth as we don't want Windows to // Disable stack growth as we don't want Windows to
@ -1229,10 +1197,10 @@ func addimports(ctxt *Link, datsect *peSection) {
out.Write32(0) out.Write32(0)
// update data directory // update data directory
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)
out.SeekSet(endoff) out.SeekSet(endoff)
} }
@ -1272,8 +1240,8 @@ func addexports(ctxt *Link) {
sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
sect.checkOffset(ctxt.Out.Offset()) sect.checkOffset(ctxt.Out.Offset())
va := int(sect.virtualAddress) va := int(sect.virtualAddress)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
vaName := va + binary.Size(&e) + nexport*4 vaName := va + binary.Size(&e) + nexport*4
vaAddr := va + binary.Size(&e) vaAddr := va + binary.Size(&e)
@ -1481,8 +1449,8 @@ func addPEBaseReloc(ctxt *Link) {
rsect.checkOffset(startoff) rsect.checkOffset(startoff)
rsect.pad(ctxt.Out, uint32(size)) rsect.pad(ctxt.Out, uint32(size))
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
} }
func (ctxt *Link) dope() { func (ctxt *Link) dope() {
@ -1528,9 +1496,9 @@ func addpersrc(ctxt *Link) {
h.pad(ctxt.Out, uint32(size)) h.pad(ctxt.Out, uint32(size))
// update data directory // update data directory
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
} }
func Asmbpe(ctxt *Link) { func Asmbpe(ctxt *Link) {

View File

@ -106,8 +106,11 @@ func testDisasm(t *testing.T, printCode bool, flags ...string) {
hello := filepath.Join(tmp, fmt.Sprintf("hello-%x.exe", hash)) hello := filepath.Join(tmp, fmt.Sprintf("hello-%x.exe", hash))
args := []string{"build", "-o", hello} args := []string{"build", "-o", hello}
args = append(args, flags...) args = append(args, flags...)
args = append(args, "testdata/fmthello.go") args = append(args, "fmthello.go")
out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput() cmd := exec.Command(testenv.GoToolPath(t), args...)
cmd.Dir = "testdata" // "Bad line" bug #36683 is sensitive to being run in the source directory
t.Logf("Running %v", cmd.Args)
out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Fatalf("go build fmthello.go: %v\n%s", err, out) t.Fatalf("go build fmthello.go: %v\n%s", err, out)
} }
@ -139,7 +142,11 @@ func testDisasm(t *testing.T, printCode bool, flags ...string) {
args = append([]string{"-S"}, args...) args = append([]string{"-S"}, args...)
} }
out, err = exec.Command(exe, args...).CombinedOutput() cmd = exec.Command(exe, args...)
cmd.Dir = "testdata" // "Bad line" bug #36683 is sensitive to being run in the source directory
out, err = cmd.CombinedOutput()
t.Logf("Running %v", cmd.Args)
if err != nil { if err != nil {
t.Fatalf("objdump fmthello.exe: %v\n%s", err, out) t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
} }

View File

@ -5,6 +5,8 @@ import "fmt"
func main() { func main() {
Println("hello, world") Println("hello, world")
if flag { if flag {
//line fmthello.go:999999
Println("bad line")
for { for {
} }
} }

View File

@ -834,10 +834,19 @@ func printTraces(w io.Writer, rpt *Report) error {
_, locations := graph.CreateNodes(prof, &graph.Options{}) _, locations := graph.CreateNodes(prof, &graph.Options{})
for _, sample := range prof.Sample { for _, sample := range prof.Sample {
var stack graph.Nodes type stk struct {
*graph.NodeInfo
inline bool
}
var stack []stk
for _, loc := range sample.Location { for _, loc := range sample.Location {
id := loc.ID nodes := locations[loc.ID]
stack = append(stack, locations[id]...) for i, n := range nodes {
// The inline flag may be inaccurate if 'show' or 'hide' filter is
// used. See https://github.com/google/pprof/issues/511.
inline := i != len(nodes)-1
stack = append(stack, stk{&n.Info, inline})
}
} }
if len(stack) == 0 { if len(stack) == 0 {
@ -875,10 +884,15 @@ func printTraces(w io.Writer, rpt *Report) error {
if d != 0 { if d != 0 {
v = v / d v = v / d
} }
fmt.Fprintf(w, "%10s %s\n", for i, s := range stack {
rpt.formatValue(v), stack[0].Info.PrintableName()) var vs, inline string
for _, s := range stack[1:] { if i == 0 {
fmt.Fprintf(w, "%10s %s\n", "", s.Info.PrintableName()) vs = rpt.formatValue(v)
}
if s.inline {
inline = " (inline)"
}
fmt.Fprintf(w, "%10s %s%s\n", vs, s.PrintableName(), inline)
} }
} }
fmt.Fprintln(w, separator) fmt.Fprintln(w, separator)

View File

@ -33,7 +33,10 @@
package profile package profile
import "errors" import (
"errors"
"fmt"
)
type buffer struct { type buffer struct {
field int // field tag field int // field tag
@ -235,7 +238,7 @@ func decodeField(b *buffer, data []byte) ([]byte, error) {
b.u64 = uint64(le32(data[:4])) b.u64 = uint64(le32(data[:4]))
data = data[4:] data = data[4:]
default: default:
return nil, errors.New("unknown wire type: " + string(b.typ)) return nil, fmt.Errorf("unknown wire type: %d", b.typ)
} }
return data, nil return data, nil

View File

@ -51,7 +51,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return // not enough arguments, e.g. called with return values of another function return // not enough arguments, e.g. called with return values of another function
} }
if fn.FullName() == "errors.As" && !pointerToInterfaceOrError(pass, call.Args[1]) { if fn.FullName() == "errors.As" && !pointerToInterfaceOrError(pass, call.Args[1]) {
pass.ReportRangef(call, "second argument to errors.As must be a pointer to an interface or a type implementing error") pass.ReportRangef(call, "second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type")
} }
}) })
return nil, nil return nil, nil

View File

@ -0,0 +1,101 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ifaceassert defines an Analyzer that flags
// impossible interface-interface type assertions.
package ifaceassert
import (
"go/ast"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `detect impossible interface-to-interface type assertions
This checker flags type assertions v.(T) and corresponding type-switch cases
in which the static type V of v is an interface that cannot possibly implement
the target interface T. This occurs when V and T contain methods with the same
name but different signatures. Example:
var v interface {
Read()
}
_ = v.(io.Reader)
The Read method in v has a different signature than the Read method in
io.Reader, so this assertion cannot succeed.
`
var Analyzer = &analysis.Analyzer{
Name: "ifaceassert",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
// assertableTo checks whether interface v can be asserted into t. It returns
// nil on success, or the first conflicting method on failure.
func assertableTo(v, t types.Type) *types.Func {
// ensure that v and t are interfaces
V, _ := v.Underlying().(*types.Interface)
T, _ := t.Underlying().(*types.Interface)
if V == nil || T == nil {
return nil
}
if f, wrongType := types.MissingMethod(V, T, false); wrongType {
return f
}
return nil
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.TypeAssertExpr)(nil),
(*ast.TypeSwitchStmt)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
var (
assert *ast.TypeAssertExpr // v.(T) expression
targets []ast.Expr // interfaces T in v.(T)
)
switch n := n.(type) {
case *ast.TypeAssertExpr:
// take care of v.(type) in *ast.TypeSwitchStmt
if n.Type == nil {
return
}
assert = n
targets = append(targets, n.Type)
case *ast.TypeSwitchStmt:
// retrieve type assertion from type switch's 'assign' field
switch t := n.Assign.(type) {
case *ast.ExprStmt:
assert = t.X.(*ast.TypeAssertExpr)
case *ast.AssignStmt:
assert = t.Rhs[0].(*ast.TypeAssertExpr)
}
// gather target types from case clauses
for _, c := range n.Body.List {
targets = append(targets, c.(*ast.CaseClause).List...)
}
}
V := pass.TypesInfo.TypeOf(assert.X)
for _, target := range targets {
T := pass.TypesInfo.TypeOf(target)
if f := assertableTo(V, T); f != nil {
pass.Reportf(
target.Pos(),
"impossible type assertion: no type can implement both %v and %v (conflicting types for %v method)",
V, T, f.Name(),
)
}
}
})
return nil, nil
}

View File

@ -0,0 +1,126 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package stringintconv defines an Analyzer that flags type conversions
// from integers to strings.
package stringintconv
import (
"fmt"
"go/ast"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for string(int) conversions
This checker flags conversions of the form string(x) where x is an integer
(but not byte or rune) type. Such conversions are discouraged because they
return the UTF-8 representation of the Unicode code point x, and not a decimal
string representation of x as one might expect. Furthermore, if x denotes an
invalid code point, the conversion cannot be statically rejected.
For conversions that intend on using the code point, consider replacing them
with string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the
string representation of the value in the desired base.
`
var Analyzer = &analysis.Analyzer{
Name: "stringintconv",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func typeName(typ types.Type) string {
if v, _ := typ.(interface{ Name() string }); v != nil {
return v.Name()
}
if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil {
return v.Obj().Name()
}
return ""
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.CallExpr)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
call := n.(*ast.CallExpr)
// Retrieve target type name.
var tname *types.TypeName
switch fun := call.Fun.(type) {
case *ast.Ident:
tname, _ = pass.TypesInfo.Uses[fun].(*types.TypeName)
case *ast.SelectorExpr:
tname, _ = pass.TypesInfo.Uses[fun.Sel].(*types.TypeName)
}
if tname == nil {
return
}
target := tname.Name()
// Check that target type T in T(v) has an underlying type of string.
T, _ := tname.Type().Underlying().(*types.Basic)
if T == nil || T.Kind() != types.String {
return
}
if s := T.Name(); target != s {
target += " (" + s + ")"
}
// Check that type V of v has an underlying integral type that is not byte or rune.
if len(call.Args) != 1 {
return
}
v := call.Args[0]
vtyp := pass.TypesInfo.TypeOf(v)
V, _ := vtyp.Underlying().(*types.Basic)
if V == nil || V.Info()&types.IsInteger == 0 {
return
}
switch V.Kind() {
case types.Byte, types.Rune, types.UntypedRune:
return
}
// Retrieve source type name.
source := typeName(vtyp)
if source == "" {
return
}
if s := V.Name(); source != s {
source += " (" + s + ")"
}
diag := analysis.Diagnostic{
Pos: n.Pos(),
Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune", source, target),
SuggestedFixes: []analysis.SuggestedFix{
{
Message: "Did you mean to convert a rune to a string?",
TextEdits: []analysis.TextEdit{
{
Pos: v.Pos(),
End: v.Pos(),
NewText: []byte("rune("),
},
{
Pos: v.End(),
End: v.End(),
NewText: []byte(")"),
},
},
},
},
}
pass.Report(diag)
})
return nil, nil
}

View File

@ -1,4 +1,4 @@
# github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12 # github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3
## explicit ## explicit
github.com/google/pprof/driver github.com/google/pprof/driver
github.com/google/pprof/internal/binutils github.com/google/pprof/internal/binutils
@ -43,7 +43,7 @@ golang.org/x/mod/zip
## explicit ## explicit
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d # golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca
## explicit ## explicit
golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags golang.org/x/tools/go/analysis/internal/analysisflags
@ -59,6 +59,7 @@ golang.org/x/tools/go/analysis/passes/copylock
golang.org/x/tools/go/analysis/passes/ctrlflow golang.org/x/tools/go/analysis/passes/ctrlflow
golang.org/x/tools/go/analysis/passes/errorsas golang.org/x/tools/go/analysis/passes/errorsas
golang.org/x/tools/go/analysis/passes/httpresponse golang.org/x/tools/go/analysis/passes/httpresponse
golang.org/x/tools/go/analysis/passes/ifaceassert
golang.org/x/tools/go/analysis/passes/inspect golang.org/x/tools/go/analysis/passes/inspect
golang.org/x/tools/go/analysis/passes/internal/analysisutil golang.org/x/tools/go/analysis/passes/internal/analysisutil
golang.org/x/tools/go/analysis/passes/loopclosure golang.org/x/tools/go/analysis/passes/loopclosure
@ -67,6 +68,7 @@ golang.org/x/tools/go/analysis/passes/nilfunc
golang.org/x/tools/go/analysis/passes/printf golang.org/x/tools/go/analysis/passes/printf
golang.org/x/tools/go/analysis/passes/shift golang.org/x/tools/go/analysis/passes/shift
golang.org/x/tools/go/analysis/passes/stdmethods golang.org/x/tools/go/analysis/passes/stdmethods
golang.org/x/tools/go/analysis/passes/stringintconv
golang.org/x/tools/go/analysis/passes/structtag golang.org/x/tools/go/analysis/passes/structtag
golang.org/x/tools/go/analysis/passes/tests golang.org/x/tools/go/analysis/passes/tests
golang.org/x/tools/go/analysis/passes/unmarshal golang.org/x/tools/go/analysis/passes/unmarshal
@ -80,6 +82,5 @@ golang.org/x/tools/go/cfg
golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/objectpath
golang.org/x/tools/go/types/typeutil golang.org/x/tools/go/types/typeutil
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 # golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
## explicit
golang.org/x/xerrors golang.org/x/xerrors
golang.org/x/xerrors/internal golang.org/x/xerrors/internal

View File

@ -15,12 +15,14 @@ import (
"golang.org/x/tools/go/analysis/passes/copylock" "golang.org/x/tools/go/analysis/passes/copylock"
"golang.org/x/tools/go/analysis/passes/errorsas" "golang.org/x/tools/go/analysis/passes/errorsas"
"golang.org/x/tools/go/analysis/passes/httpresponse" "golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/ifaceassert"
"golang.org/x/tools/go/analysis/passes/loopclosure" "golang.org/x/tools/go/analysis/passes/loopclosure"
"golang.org/x/tools/go/analysis/passes/lostcancel" "golang.org/x/tools/go/analysis/passes/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc" "golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/printf" "golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift" "golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/go/analysis/passes/stdmethods" "golang.org/x/tools/go/analysis/passes/stdmethods"
"golang.org/x/tools/go/analysis/passes/stringintconv"
"golang.org/x/tools/go/analysis/passes/structtag" "golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/tests" "golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal" "golang.org/x/tools/go/analysis/passes/unmarshal"
@ -43,12 +45,14 @@ func main() {
copylock.Analyzer, copylock.Analyzer,
errorsas.Analyzer, errorsas.Analyzer,
httpresponse.Analyzer, httpresponse.Analyzer,
ifaceassert.Analyzer,
loopclosure.Analyzer, loopclosure.Analyzer,
lostcancel.Analyzer, lostcancel.Analyzer,
nilfunc.Analyzer, nilfunc.Analyzer,
printf.Analyzer, printf.Analyzer,
shift.Analyzer, shift.Analyzer,
stdmethods.Analyzer, stdmethods.Analyzer,
stringintconv.Analyzer,
structtag.Analyzer, structtag.Analyzer,
tests.Analyzer, tests.Analyzer,
unmarshal.Analyzer, unmarshal.Analyzer,

View File

@ -106,7 +106,7 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
case Ed25519: case Ed25519:
sigType = signatureEd25519 sigType = signatureEd25519
default: default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %#04x", signatureAlgorithm) return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
} }
switch signatureAlgorithm { switch signatureAlgorithm {
case PKCS1WithSHA1, ECDSAWithSHA1: case PKCS1WithSHA1, ECDSAWithSHA1:
@ -120,7 +120,7 @@ func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType
case Ed25519: case Ed25519:
hash = directSigning hash = directSigning
default: default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %#04x", signatureAlgorithm) return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
} }
return sigType, hash, nil return sigType, hash, nil
} }

View File

@ -62,7 +62,7 @@ func TestSignatureSelection(t *testing.T) {
t.Errorf("test[%d]: unexpected selectSignatureScheme error: %v", testNo, err) t.Errorf("test[%d]: unexpected selectSignatureScheme error: %v", testNo, err)
} }
if test.expectedSigAlg != sigAlg { if test.expectedSigAlg != sigAlg {
t.Errorf("test[%d]: expected signature scheme %#x, got %#x", testNo, test.expectedSigAlg, sigAlg) t.Errorf("test[%d]: expected signature scheme %v, got %v", testNo, test.expectedSigAlg, sigAlg)
} }
sigType, hashFunc, err := typeAndHashFromSignatureScheme(sigAlg) sigType, hashFunc, err := typeAndHashFromSignatureScheme(sigAlg)
if err != nil { if err != nil {
@ -115,7 +115,7 @@ func TestSignatureSelection(t *testing.T) {
for testNo, test := range badTests { for testNo, test := range badTests {
sigAlg, err := selectSignatureScheme(test.tlsVersion, test.cert, test.peerSigAlgs) sigAlg, err := selectSignatureScheme(test.tlsVersion, test.cert, test.peerSigAlgs)
if err == nil { if err == nil {
t.Errorf("test[%d]: unexpected success, got %#x", testNo, sigAlg) t.Errorf("test[%d]: unexpected success, got %v", testNo, sigAlg)
} }
} }
} }
@ -129,7 +129,7 @@ func TestLegacyTypeAndHash(t *testing.T) {
t.Errorf("RSA: expected signature type %#x, got %#x", expectedSigType, sigType) t.Errorf("RSA: expected signature type %#x, got %#x", expectedSigType, sigType)
} }
if expectedHashFunc := crypto.MD5SHA1; expectedHashFunc != hashFunc { if expectedHashFunc := crypto.MD5SHA1; expectedHashFunc != hashFunc {
t.Errorf("RSA: expected hash %#x, got %#x", expectedHashFunc, sigType) t.Errorf("RSA: expected hash %#x, got %#x", expectedHashFunc, hashFunc)
} }
sigType, hashFunc, err = legacyTypeAndHashFromPublicKey(testECDSAPrivateKey.Public()) sigType, hashFunc, err = legacyTypeAndHashFromPublicKey(testECDSAPrivateKey.Public())
@ -140,7 +140,7 @@ func TestLegacyTypeAndHash(t *testing.T) {
t.Errorf("ECDSA: expected signature type %#x, got %#x", expectedSigType, sigType) t.Errorf("ECDSA: expected signature type %#x, got %#x", expectedSigType, sigType)
} }
if expectedHashFunc := crypto.SHA1; expectedHashFunc != hashFunc { if expectedHashFunc := crypto.SHA1; expectedHashFunc != hashFunc {
t.Errorf("ECDSA: expected hash %#x, got %#x", expectedHashFunc, sigType) t.Errorf("ECDSA: expected hash %#x, got %#x", expectedHashFunc, hashFunc)
} }
// Ed25519 is not supported by TLS 1.0 and 1.1. // Ed25519 is not supported by TLS 1.0 and 1.1.
@ -156,13 +156,13 @@ func TestSupportedSignatureAlgorithms(t *testing.T) {
for _, sigAlg := range supportedSignatureAlgorithms { for _, sigAlg := range supportedSignatureAlgorithms {
sigType, hash, err := typeAndHashFromSignatureScheme(sigAlg) sigType, hash, err := typeAndHashFromSignatureScheme(sigAlg)
if err != nil { if err != nil {
t.Errorf("%#04x: unexpected error: %v", sigAlg, err) t.Errorf("%v: unexpected error: %v", sigAlg, err)
} }
if sigType == 0 { if sigType == 0 {
t.Errorf("%#04x: missing signature type", sigAlg) t.Errorf("%v: missing signature type", sigAlg)
} }
if hash == 0 && sigAlg != Ed25519 { if hash == 0 && sigAlg != Ed25519 {
t.Errorf("%#04x: missing hash", sigAlg) t.Errorf("%v: missing hash", sigAlg)
} }
} }
} }

View File

@ -299,6 +299,8 @@ type ClientSessionCache interface {
Put(sessionKey string, cs *ClientSessionState) Put(sessionKey string, cs *ClientSessionState)
} }
//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
// SignatureScheme identifies a signature algorithm supported by TLS. See // SignatureScheme identifies a signature algorithm supported by TLS. See
// RFC 8446, Section 4.2.3. // RFC 8446, Section 4.2.3.
type SignatureScheme uint16 type SignatureScheme uint16

View File

@ -0,0 +1,116 @@
// Code generated by "stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
package tls
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[PKCS1WithSHA256-1025]
_ = x[PKCS1WithSHA384-1281]
_ = x[PKCS1WithSHA512-1537]
_ = x[PSSWithSHA256-2052]
_ = x[PSSWithSHA384-2053]
_ = x[PSSWithSHA512-2054]
_ = x[ECDSAWithP256AndSHA256-1027]
_ = x[ECDSAWithP384AndSHA384-1283]
_ = x[ECDSAWithP521AndSHA512-1539]
_ = x[Ed25519-2055]
_ = x[PKCS1WithSHA1-513]
_ = x[ECDSAWithSHA1-515]
}
const (
_SignatureScheme_name_0 = "PKCS1WithSHA1"
_SignatureScheme_name_1 = "ECDSAWithSHA1"
_SignatureScheme_name_2 = "PKCS1WithSHA256"
_SignatureScheme_name_3 = "ECDSAWithP256AndSHA256"
_SignatureScheme_name_4 = "PKCS1WithSHA384"
_SignatureScheme_name_5 = "ECDSAWithP384AndSHA384"
_SignatureScheme_name_6 = "PKCS1WithSHA512"
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
)
var (
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
)
func (i SignatureScheme) String() string {
switch {
case i == 513:
return _SignatureScheme_name_0
case i == 515:
return _SignatureScheme_name_1
case i == 1025:
return _SignatureScheme_name_2
case i == 1027:
return _SignatureScheme_name_3
case i == 1281:
return _SignatureScheme_name_4
case i == 1283:
return _SignatureScheme_name_5
case i == 1537:
return _SignatureScheme_name_6
case i == 1539:
return _SignatureScheme_name_7
case 2052 <= i && i <= 2055:
i -= 2052
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
default:
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[CurveP256-23]
_ = x[CurveP384-24]
_ = x[CurveP521-25]
_ = x[X25519-29]
}
const (
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
_CurveID_name_1 = "X25519"
)
var (
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
)
func (i CurveID) String() string {
switch {
case 23 <= i && i <= 25:
i -= 23
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
case i == 29:
return _CurveID_name_1
default:
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[NoClientCert-0]
_ = x[RequestClientCert-1]
_ = x[RequireAnyClientCert-2]
_ = x[VerifyClientCertIfGiven-3]
_ = x[RequireAndVerifyClientCert-4]
}
const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertVerifyClientCertIfGivenRequireAndVerifyClientCert"
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
func (i ClientAuthType) String() string {
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
}

View File

@ -621,16 +621,14 @@ func TestBuildingWindowsGUI(t *testing.T) {
} }
defer f.Close() defer f.Close()
const _IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
switch oh := f.OptionalHeader.(type) { switch oh := f.OptionalHeader.(type) {
case *OptionalHeader32: case *OptionalHeader32:
if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI { if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, _IMAGE_SUBSYSTEM_WINDOWS_GUI) t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
} }
case *OptionalHeader64: case *OptionalHeader64:
if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI { if oh.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_GUI {
t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, _IMAGE_SUBSYSTEM_WINDOWS_GUI) t.Errorf("unexpected Subsystem value: have %d, but want %d", oh.Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI)
} }
default: default:
t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh) t.Fatalf("unexpected OptionalHeader type: have %T, but want *pe.OptionalHeader32 or *pe.OptionalHeader64", oh)

View File

@ -129,3 +129,56 @@ const (
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
) )
// Values of IMAGE_FILE_HEADER.Characteristics. These can be combined together.
const (
IMAGE_FILE_RELOCS_STRIPPED = 0x0001
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080
IMAGE_FILE_32BIT_MACHINE = 0x0100
IMAGE_FILE_DEBUG_STRIPPED = 0x0200
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800
IMAGE_FILE_SYSTEM = 0x1000
IMAGE_FILE_DLL = 0x2000
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
)
// OptionalHeader64.Subsystem and OptionalHeader32.Subsystem values.
const (
IMAGE_SUBSYSTEM_UNKNOWN = 0
IMAGE_SUBSYSTEM_NATIVE = 1
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
IMAGE_SUBSYSTEM_OS2_CUI = 5
IMAGE_SUBSYSTEM_POSIX_CUI = 7
IMAGE_SUBSYSTEM_NATIVE_WINDOWS = 8
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12
IMAGE_SUBSYSTEM_EFI_ROM = 13
IMAGE_SUBSYSTEM_XBOX = 14
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
)
// OptionalHeader64.DllCharacteristics and OptionalHeader32.DllCharacteristics
// values. These can be combined together.
const (
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = 0x0080
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200
IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400
IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800
IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000
IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
)

View File

@ -70,7 +70,7 @@ func Is(err, target error) bool {
// setting target. // setting target.
// //
// An error type might provide an As method so it can be treated as if it were a // An error type might provide an As method so it can be treated as if it were a
// a different error type. // different error type.
// //
// As panics if target is not a non-nil pointer to either a type that implements // As panics if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. // error, or to any interface type.

View File

@ -4,7 +4,7 @@ go 1.14
require ( require (
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
golang.org/x/net v0.0.0-20200219183655-46282727080f golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
golang.org/x/text v0.3.3-0.20191031172631-4b67af870c6f // indirect golang.org/x/text v0.3.3-0.20191031172631-4b67af870c6f // indirect
) )

View File

@ -2,8 +2,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200219183655-46282727080f h1:dB42wwhNuwPvh8f+5zZWNcU+F2Xs/B9wXXwvUCOH7r8= golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200219183655-46282727080f/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ= golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ=

View File

@ -20,6 +20,7 @@ const (
) )
// MaxLen is the maximum length of the string to be searched for (argument b) in Index. // MaxLen is the maximum length of the string to be searched for (argument b) in Index.
// If MaxLen is not 0, make sure MaxLen >= 4.
var MaxLen int var MaxLen int
// FIXME: the logic of HashStrBytes, HashStrRevBytes, IndexRabinKarpBytes and HashStr, HashStrRev, // FIXME: the logic of HashStrBytes, HashStrRevBytes, IndexRabinKarpBytes and HashStr, HashStrRev,

View File

@ -16,8 +16,42 @@ func Index(a, b []byte) int {
// IndexString returns the index of the first instance of b in a, or -1 if b is not present in a. // IndexString returns the index of the first instance of b in a, or -1 if b is not present in a.
// Requires 2 <= len(b) <= MaxLen. // Requires 2 <= len(b) <= MaxLen.
func IndexString(a, b string) int { func IndexString(s, substr string) int {
panic("unimplemented") // This is a partial copy of strings.Index, here because bytes.IndexAny and bytes.LastIndexAny
// call bytealg.IndexString. Some platforms have an optimized assembly version of this function.
// This implementation is used for those that do not. Although the pure Go implementation here
// works for the case of len(b) > MaxLen, we do not require that its assembly implementation also
// supports the case of len(b) > MaxLen. And we do not guarantee that this function supports the
// case of len(b) > MaxLen.
n := len(substr)
c0 := substr[0]
c1 := substr[1]
i := 0
t := len(s) - n + 1
fails := 0
for i < t {
if s[i] != c0 {
o := IndexByteString(s[i:t], c0)
if o < 0 {
return -1
}
i += o
}
if s[i+1] == c1 && s[i:i+n] == substr {
return i
}
i++
fails++
if fails >= 4+i>>4 && i < t {
// See comment in src/bytes/bytes.go.
j := IndexRabinKarp(s[i:], substr)
if j < 0 {
return -1
}
return i + j
}
}
return -1
} }
// Cutover reports the number of failures of IndexByte we should tolerate // Cutover reports the number of failures of IndexByte we should tolerate

View File

@ -412,10 +412,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
sect.Relocs[i].Type = rel.Rtype sect.Relocs[i].Type = rel.Rtype
sect.Relocs[i].Length = rel.Rsize&0x3F + 1 sect.Relocs[i].Length = rel.Rsize&0x3F + 1
if rel.Rsize&0x80 == 1 { if rel.Rsize&0x80 != 0 {
sect.Relocs[i].Signed = true sect.Relocs[i].Signed = true
} }
if rel.Rsize&0x40 == 1 { if rel.Rsize&0x40 != 0 {
sect.Relocs[i].InstructionFixed = true sect.Relocs[i].InstructionFixed = true
} }
@ -428,10 +428,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)] sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
sect.Relocs[i].Type = rel.Rtype sect.Relocs[i].Type = rel.Rtype
sect.Relocs[i].Length = rel.Rsize&0x3F + 1 sect.Relocs[i].Length = rel.Rsize&0x3F + 1
if rel.Rsize&0x80 == 1 { if rel.Rsize&0x80 != 0 {
sect.Relocs[i].Signed = true sect.Relocs[i].Signed = true
} }
if rel.Rsize&0x40 == 1 { if rel.Rsize&0x40 != 0 {
sect.Relocs[i].InstructionFixed = true sect.Relocs[i].InstructionFixed = true
} }
} }

View File

@ -236,6 +236,10 @@ type WriterAt interface {
// ReadByte reads and returns the next byte from the input or // ReadByte reads and returns the next byte from the input or
// any error encountered. If ReadByte returns an error, no input // any error encountered. If ReadByte returns an error, no input
// byte was consumed, and the returned byte value is undefined. // byte was consumed, and the returned byte value is undefined.
//
// ReadByte provides an efficient interface for byte-at-time
// processing. A Reader that does not implement ByteReader
// can be wrapped using bufio.NewReader to add this method.
type ByteReader interface { type ByteReader interface {
ReadByte() (byte, error) ReadByte() (byte, error)
} }

View File

@ -65,6 +65,7 @@ var builtinTypesLower = map[string]string{
".jpeg": "image/jpeg", ".jpeg": "image/jpeg",
".jpg": "image/jpeg", ".jpg": "image/jpeg",
".js": "text/javascript; charset=utf-8", ".js": "text/javascript; charset=utf-8",
".json": "application/json",
".mjs": "text/javascript; charset=utf-8", ".mjs": "text/javascript; charset=utf-8",
".pdf": "application/pdf", ".pdf": "application/pdf",
".png": "image/png", ".png": "image/png",

View File

@ -441,6 +441,14 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
t.Skip("both IPv4 and IPv6 are required") t.Skip("both IPv4 and IPv6 are required")
} }
var readDeadline time.Time
if td, ok := t.Deadline(); ok {
const arbitraryCleanupMargin = 1 * time.Second
readDeadline = td.Add(-arbitraryCleanupMargin)
} else {
readDeadline = time.Now().Add(5 * time.Second)
}
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
handler := func(dss *dualStackServer, ln Listener) { handler := func(dss *dualStackServer, ln Listener) {
@ -450,7 +458,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// The client should close itself, without sending data. // The client should close itself, without sending data.
c.SetReadDeadline(time.Now().Add(1 * time.Second)) c.SetReadDeadline(readDeadline)
var b [1]byte var b [1]byte
if _, err := c.Read(b[:]); err != io.EOF { if _, err := c.Read(b[:]); err != io.EOF {
t.Errorf("got %v; want %v", err, io.EOF) t.Errorf("got %v; want %v", err, io.EOF)

View File

@ -89,8 +89,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) {
r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v) r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v)
} }
// TODO: cookies. parsing them isn't exported, though.
uriStr := params["REQUEST_URI"] uriStr := params["REQUEST_URI"]
if uriStr == "" { if uriStr == "" {
// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING. // Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.

View File

@ -4135,10 +4135,19 @@ func TestServerConnState(t *testing.T) {
doRequests() doRequests()
timer := time.NewTimer(5 * time.Second) stateDelay := 5 * time.Second
if deadline, ok := t.Deadline(); ok {
// Allow an arbitrarily long delay.
// This test was observed to be flaky on the darwin-arm64-corellium builder,
// so we're increasing the deadline to see if it starts passing.
// See https://golang.org/issue/37322.
const arbitraryCleanupMargin = 1 * time.Second
stateDelay = time.Until(deadline) - arbitraryCleanupMargin
}
timer := time.NewTimer(stateDelay)
select { select {
case <-timer.C: case <-timer.C:
t.Errorf("Timed out waiting for connection to change state.") t.Errorf("Timed out after %v waiting for connection to change state.", stateDelay)
case <-complete: case <-complete:
timer.Stop() timer.Stop()
} }

View File

@ -158,6 +158,8 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr {
// is slower but more general and is used for hashing interface types // is slower but more general and is used for hashing interface types
// (called from interhash or nilinterhash, above) or for hashing in // (called from interhash or nilinterhash, above) or for hashing in
// maps generated by reflect.MapOf (reflect_typehash, below). // maps generated by reflect.MapOf (reflect_typehash, below).
// Note: this function must match the compiler generated
// functions exactly. See issue 37716.
func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr { func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
if t.tflag&tflagRegularMemory != 0 { if t.tflag&tflagRegularMemory != 0 {
// Handle ptr sizes specially, see issue 37086. // Handle ptr sizes specially, see issue 37086.
@ -195,12 +197,28 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
return h return h
case kindStruct: case kindStruct:
s := (*structtype)(unsafe.Pointer(t)) s := (*structtype)(unsafe.Pointer(t))
memStart := uintptr(0)
memEnd := uintptr(0)
for _, f := range s.fields { for _, f := range s.fields {
// TODO: maybe we could hash several contiguous fields all at once. if memEnd > memStart && (f.name.isBlank() || f.offset() != memEnd || f.typ.tflag&tflagRegularMemory == 0) {
// flush any pending regular memory hashing
h = memhash(add(p, memStart), h, memEnd-memStart)
memStart = memEnd
}
if f.name.isBlank() { if f.name.isBlank() {
continue continue
} }
if f.typ.tflag&tflagRegularMemory == 0 {
h = typehash(f.typ, add(p, f.offset()), h) h = typehash(f.typ, add(p, f.offset()), h)
continue
}
if memStart == memEnd {
memStart = f.offset()
}
memEnd = f.offset() + f.typ.size
}
if memEnd > memStart {
h = memhash(add(p, memStart), h, memEnd-memStart)
} }
return h return h
default: default:

View File

@ -10,18 +10,18 @@ func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
// Check that (*[n]elem)(p) is appropriately aligned. // Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign? // TODO(mdempsky): What about fieldAlign?
if uintptr(p)&(uintptr(elem.align)-1) != 0 { if uintptr(p)&(uintptr(elem.align)-1) != 0 {
throw("checkptr: unsafe pointer conversion") throw("checkptr: misaligned pointer conversion")
} }
// Check that (*[n]elem)(p) doesn't straddle multiple heap objects. // Check that (*[n]elem)(p) doesn't straddle multiple heap objects.
if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) { if size := n * elem.size; size > 1 && checkptrBase(p) != checkptrBase(add(p, size-1)) {
throw("checkptr: unsafe pointer conversion") throw("checkptr: converted pointer straddles multiple allocations")
} }
} }
func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) { func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
if 0 < uintptr(p) && uintptr(p) < minLegalPointer { if 0 < uintptr(p) && uintptr(p) < minLegalPointer {
throw("checkptr: unsafe pointer arithmetic") throw("checkptr: pointer arithmetic computed bad pointer value")
} }
// Check that if the computed pointer p points into a heap // Check that if the computed pointer p points into a heap
@ -38,7 +38,7 @@ func checkptrArithmetic(p unsafe.Pointer, originals []unsafe.Pointer) {
} }
} }
throw("checkptr: unsafe pointer arithmetic") throw("checkptr: pointer arithmetic result points to invalid allocation")
} }
// checkptrBase returns the base address for the allocation containing // checkptrBase returns the base address for the allocation containing

View File

@ -24,10 +24,10 @@ func TestCheckPtr(t *testing.T) {
cmd string cmd string
want string want string
}{ }{
{"CheckPtrAlignment", "fatal error: checkptr: unsafe pointer conversion\n"}, {"CheckPtrAlignment", "fatal error: checkptr: misaligned pointer conversion\n"},
{"CheckPtrArithmetic", "fatal error: checkptr: unsafe pointer arithmetic\n"}, {"CheckPtrArithmetic", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
{"CheckPtrSize", "fatal error: checkptr: unsafe pointer conversion\n"}, {"CheckPtrSize", "fatal error: checkptr: converted pointer straddles multiple allocations\n"},
{"CheckPtrSmall", "fatal error: checkptr: unsafe pointer arithmetic\n"}, {"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"},
} }
for _, tc := range testCases { for _, tc := range testCases {

View File

@ -15,6 +15,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"sync" "sync"
"syscall" "syscall"
"testing" "testing"
@ -288,6 +289,12 @@ func TestSignalExitStatus(t *testing.T) {
} }
func TestSignalIgnoreSIGTRAP(t *testing.T) { func TestSignalIgnoreSIGTRAP(t *testing.T) {
if runtime.GOOS == "openbsd" {
if bn := testenv.Builder(); strings.HasSuffix(bn, "-62") || strings.HasSuffix(bn, "-64") {
testenv.SkipFlaky(t, 17496)
}
}
output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP") output := runTestProg(t, "testprognet", "SignalIgnoreSIGTRAP")
want := "OK\n" want := "OK\n"
if output != want { if output != want {

Some files were not shown because too many files have changed in this diff Show More