[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.
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 as, isASCII := makeASCIISet(chars); isASCII {
for i, c := range s {
@ -197,14 +220,26 @@ func IndexAny(s []byte, chars string) int {
for i := 0; i < len(s); i += width {
r := rune(s[i])
if r < utf8.RuneSelf {
width = 1
} else {
r, width = utf8.DecodeRune(s[i:])
}
for _, ch := range chars {
if r == ch {
if bytealg.IndexByteString(chars, s[i]) >= 0 {
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
@ -229,14 +264,60 @@ func LastIndexAny(s []byte, chars string) int {
return -1
}
}
for i := len(s); i > 0; {
r, size := utf8.DecodeLastRune(s[:i])
i -= size
for _, c := range chars {
if r == c {
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; {
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r == cr {
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
}

View File

@ -169,6 +169,7 @@ var indexAnyTests = []BinOpTest{
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 0},
{"abc", "xyz", -1},
{"abc", "xcz", 2},
@ -179,6 +180,7 @@ var indexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 4},
{"012\x80bcb\x80210", "\xffb", 3},
{"0123456\xcf\x80abc", "\xcfb\x80", 10},
}
var lastIndexAnyTests = []BinOpTest{
@ -187,6 +189,7 @@ var lastIndexAnyTests = []BinOpTest{
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 2},
{"abc", "xyz", -1},
{"abc", "ab", 1},
@ -197,6 +200,7 @@ var lastIndexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 6},
{"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
@ -1890,10 +1894,10 @@ func BenchmarkBytesCompare(b *testing.B) {
}
func BenchmarkIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 4096) // Never matches set
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {
for j := 1; j <= 16; j <<= 1 {
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++ {
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) {
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {

View File

@ -1064,7 +1064,7 @@ label1:
// VSX AND, XX3-form
// <MNEMONIC> XA,XB,XT produces
// <mnemonic> XT,XA,XB
XXLANDQ VS0,VS1,VS32
XXLAND VS0,VS1,VS32
XXLANDC VS0,VS1,VS32
XXLEQV VS0,VS1,VS32
XXLNAND VS0,VS1,VS32
@ -1093,6 +1093,11 @@ label1:
// <mnemonic> XT,XB,UIM
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
// <MNEMONIC> XA,XB,DM,XT produces
// <mnemonic> XT,XA,XB,DM

View File

@ -22,6 +22,7 @@ import (
"regexp"
"sort"
"strings"
"unicode"
)
var (
@ -802,6 +803,28 @@ func (p *Package) packedAttribute() string {
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
// from Go so that they are callable from C.
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 {
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 += ")"
if len(exp.Doc) > 0 {
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, "\nCGO_NO_SANITIZE_THREAD")
fmt.Fprintf(fgcc, "\n%s\n", s)
fmt.Fprintf(fgcc, "{\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) {
fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
}
if fn.Recv != nil {
fmt.Fprintf(fgcc, "\ta.recv = recv;\n")
fmt.Fprintf(fgcc, "\t_cgo_a.recv = recv;\n")
}
forFieldList(fntype.Params,
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, "\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_release_context(_cgo_ctxt);\n")
if gccResult != "void" {
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 {
forFieldList(fntype.Results,
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")
}

View File

@ -77,8 +77,6 @@ Flags:
-lang version
Set language version to compile, as in -lang=go1.12.
Default is current version.
-largemodel
Generate code that assumes a large memory model.
-linkobj file
Write linker-specific object to file and compiler-specific
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
// the hash of a value of type t.
// Note: the generated function must match runtime.typehash exactly.
func genhash(t *types.Type) *obj.LSym {
switch algtype(t) {
default:

View File

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

View File

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

View File

@ -585,28 +585,44 @@ 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 {
switch mode {
case FErr: // This is for the user
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 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:
return s.Pkg.Name + "." + s.Name
b.WriteString(s.Pkg.Name)
b.WriteByte('.')
b.WriteString(s.Name)
return
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:
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 {
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{
@ -652,16 +670,16 @@ var basicnames = []string{
TBLANK: "blank",
}
var tconvBufferPool = sync.Pool{
var fmtBufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func tconv(t *types.Type, flag FmtFlag, mode fmtMode) string {
buf := tconvBufferPool.Get().(*bytes.Buffer)
buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer tconvBufferPool.Put(buf)
defer fmtBufferPool.Put(buf)
tconv2(buf, t, flag, mode, nil)
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:
t = types.Types[t.Etype]
default:
b.WriteString(sconv(t.Sym, FmtShort, mode))
sconv2(b, t.Sym, FmtShort, mode)
return
}
}
@ -718,15 +736,16 @@ func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode fmtMode, visited
case FTypeId, FTypeIdName:
if flag&FmtShort != 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
}
b.WriteString(sconv(t.Sym, FmtShort, mode))
sconv2(b, t.Sym, FmtShort, mode)
return
}
if mode == FTypeIdName {
b.WriteString(sconv(t.Sym, FmtUnsigned, mode))
sconv2(b, t.Sym, FmtUnsigned, mode)
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
}
@ -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.
break
case types.IsExported(f.Sym.Name):
b.WriteString(sconv(f.Sym, FmtShort, mode))
sconv2(b, f.Sym, FmtShort, mode)
default:
flag1 := FmtLeft
if flag&FmtUnsigned != 0 {
flag1 = FmtUnsigned
}
b.WriteString(sconv(f.Sym, flag1, mode))
sconv2(b, f.Sym, flag1, mode)
}
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")
if t.Sym != nil {
b.WriteByte(' ')
b.WriteString(smodeString(t.Sym, mode))
sconv2(b, t.Sym, 0, mode)
}
case TUNSAFEPTR:
@ -1731,9 +1750,30 @@ func sconv(s *types.Sym, flag FmtFlag, mode fmtMode) string {
if s.Name == "_" {
return "_"
}
buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer fmtBufferPool.Put(buf)
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) {

View File

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

View File

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

View File

@ -1070,7 +1070,7 @@ func loadsys() {
typecheckok = true
typs := runtimeTypes()
for _, d := range runtimeDecls {
for _, d := range &runtimeDecls {
sym := Runtimepkg.Lookup(d.name)
typ := typs[d.typ]
switch d.tag {
@ -1373,7 +1373,7 @@ var concurrentFlagOK = [256]bool{
}
func concurrentBackendAllowed() bool {
for i, x := range Debug {
for i, x := range &Debug {
if x != 0 && !concurrentFlagOK[i] {
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)
var index [3]*Node
for i, x := range expr.Index {
for i, x := range &expr.Index {
if x != nil {
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 {
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 {
continue
}
@ -705,12 +705,6 @@ func (lv *Liveness) markUnsafePoints() {
v = v.Args[0]
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:
// Args[0] is the address of the write
// barrier control. Ignore Args[1],

View File

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

View File

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

View File

@ -390,7 +390,7 @@ func FlushLoggedOpts(ctxt *obj.Link, slashPkgPath string) {
var w io.WriteCloser
if slashPkgPath == "" {
slashPkgPath = string(0)
slashPkgPath = "\000"
}
subdirpath := filepath.Join(dest, pathEscape(slashPkgPath))
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 {
return e.i
}
func (e Edge) String() string {
return fmt.Sprintf("{%v,%d}", e.b, e.i)
}
// kind controls successors
// ------------------------------------------

View File

@ -442,6 +442,7 @@ var passes = [...]pass{
{name: "insert resched checks", fn: insertLoopReschedChecks,
disabled: objabi.Preemptibleloops_enabled == 0}, // insert resched checks in loops.
{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 cse", fn: cse},
{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)
(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)
(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)
@ -611,34 +607,22 @@
// fold constants into memory operations
// 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
// have potentially two live values (ptr and (ADDQconst [off] ptr)) instead of one.
// the ADDLconst get eliminated, we still have to compute the ADDLconst and we now
// have potentially two live values (ptr and (ADDLconst [off] ptr)) instead of one.
// 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)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+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+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+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+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 [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.
(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 [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.
// Note: we turn off this merging for operations on globals when building
// position-independent code (when Flag_shared is set).
@ -672,31 +656,9 @@
&& (ptr.Op != OpSB || !config.ctxt.Flag_shared) ->
(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)
&& 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)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)
&& 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)
@ -706,97 +668,20 @@
((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) ->
((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)
&& 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)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
((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)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|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)
(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)
&& y.Uses==1 && l.Uses==1 && clobber(y, l) && validValAndOff(c,off) ->
((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
(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) ->
(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.
(LT (InvertFlags cmp) yes no) -> (GT cmp yes no)
(GT (InvertFlags cmp) yes no) -> (LT cmp yes no)
@ -1039,23 +934,27 @@
// TEST %reg,%reg is shorter than CMP
(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.
// There are many ways these combinations could occur. This is
// designed to match the way encoding/binary.LittleEndian does it.
(ORL x0:(MOVBload [i0] {s} p mem)
s0:(SHLLconst [8] x1:(MOVBload [i1] {s} p mem)))
(ORL x0:(MOVBload [i0] {s} p0 mem)
s0:(SHLLconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& s0.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
x0:(MOVWload [i0] {s} p mem)
s0:(SHLLconst [16] x1:(MOVBload [i2] {s} p mem)))
s1:(SHLLconst [24] x2:(MOVBload [i3] {s} p mem)))
x0:(MOVWload [i0] {s} p0 mem)
s0:(SHLLconst [16] x1:(MOVBload [i2] {s} p1 mem)))
s1:(SHLLconst [24] x2:(MOVBload [i3] {s} p2 mem)))
&& i2 == i0+2
&& i3 == i0+3
&& x0.Uses == 1
@ -1064,126 +963,84 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& o0.Uses == 1
&& same(p0, p1, 1)
&& same(p1, p2, 1)
&& mergePoint(b,x0,x1,x2) != nil
&& clobber(x0, x1, x2, s0, s1, o0)
-> @mergePoint(b,x0,x1,x2) (MOVLload [i0] {s} p 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)
-> @mergePoint(b,x0,x1,x2) (MOVLload [i0] {s} p0 mem)
// 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
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem))
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVBstoreconst [a] {s} p1 x:(MOVBstoreconst [c] {s} p0 mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem))
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [a] {s} p1 x:(MOVWstoreconst [c] {s} p0 mem))
&& x.Uses == 1
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p 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)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
// 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
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i-1] {s} p w mem)
(MOVBstore [i] {s} p w x:(MOVBstore {s} [i+1] p (SHR(W|L)const [8] w) mem))
-> (MOVWstore [i-1] {s} p0 w mem)
(MOVBstore [i] {s} p1 w x:(MOVBstore {s} [i+1] p0 (SHR(W|L)const [8] w) mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i] {s} p w mem)
(MOVBstore [i] {s} p (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p w0:(SHRLconst [j-8] w) mem))
-> (MOVWstore [i] {s} p0 w mem)
(MOVBstore [i] {s} p1 (SHRLconst [j] w) x:(MOVBstore [i-1] {s} p0 w0:(SHRLconst [j-8] w) mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i-1] {s} p w0 mem)
(MOVWstore [i] {s} p (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p w mem))
-> (MOVWstore [i-1] {s} p0 w0 mem)
(MOVWstore [i] {s} p1 (SHRLconst [16] w) x:(MOVWstore [i-2] {s} p0 w mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVLstore [i-2] {s} p w mem)
(MOVWstore [i] {s} p (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p w0:(SHRLconst [j-16] w) mem))
-> (MOVLstore [i-2] {s} p0 w mem)
(MOVWstore [i] {s} p1 (SHRLconst [j] w) x:(MOVWstore [i-2] {s} p0 w0:(SHRLconst [j-16] w) mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& 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))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i-1] {s} p idx w mem)
(MOVBstoreidx1 [i] {s} p idx w x:(MOVBstoreidx1 [i+1] {s} p idx (SHR(L|W)const [8] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVWstoreidx1 [i] {s} p idx w mem)
(MOVBstoreidx1 [i] {s} p idx (SHRLconst [j] w) x:(MOVBstoreidx1 [i-1] {s} p idx w0:(SHRLconst [j-8] w) mem))
&& x.Uses == 1
&& 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)
// Move constant offsets from LEALx up into load. This lets the above combining
// rules discover indexed load-combining instances.
(MOV(B|W|L)load [i0] {s0} l:(LEAL1 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)load [i0+i1] {s0} (LEAL1 <l.Type> [0] {s1} x y) mem)
(MOV(B|W|L)load [i0] {s0} l:(LEAL2 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)load [i0+i1] {s0} (LEAL2 <l.Type> [0] {s1} x y) mem)
(MOV(B|W|L)load [i0] {s0} l:(LEAL4 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)load [i0+i1] {s0} (LEAL4 <l.Type> [0] {s1} x y) mem)
(MOV(B|W|L)load [i0] {s0} l:(LEAL8 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)load [i0+i1] {s0} (LEAL8 <l.Type> [0] {s1} x y) mem)
(MOVWstoreidx2 [i] {s} p idx (SHRLconst [16] w) x:(MOVWstoreidx2 [i-2] {s} p idx w mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w mem)
(MOVWstoreidx2 [i] {s} p idx (SHRLconst [j] w) x:(MOVWstoreidx2 [i-2] {s} p idx w0:(SHRLconst [j-16] w) mem))
&& x.Uses == 1
&& clobber(x)
-> (MOVLstoreidx1 [i-2] {s} p (SHLLconst <idx.Type> [1] idx) w0 mem)
(MOV(B|W|L)store [i0] {s0} l:(LEAL1 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)store [i0+i1] {s0} (LEAL1 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L)store [i0] {s0} l:(LEAL2 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)store [i0+i1] {s0} (LEAL2 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L)store [i0] {s0} l:(LEAL4 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L)store [i0+i1] {s0} (LEAL4 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L)store [i0] {s0} l:(LEAL8 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (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
// 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
(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)
(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)
@ -1166,86 +1160,6 @@
&& is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
((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
(LEAQ [off1] {sym1} (LEAQ [off2] {sym2} x)) && is32Bit(off1+off2) && canMergeSym(sym1, sym2) ->
(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) ->
(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.
(LT (InvertFlags cmp) yes no) -> (GT 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)
(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.
@ -1552,60 +1480,65 @@
// Little-endian loads
(ORL x0:(MOVBload [i0] {s} p mem)
sh:(SHLLconst [8] x1:(MOVBload [i1] {s} p mem)))
(ORL x0:(MOVBload [i0] {s} p0 mem)
sh:(SHLLconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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)
sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p mem)))
(ORQ x0:(MOVBload [i0] {s} p0 mem)
sh:(SHLQconst [8] x1:(MOVBload [i1] {s} p1 mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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)
sh:(SHLLconst [16] x1:(MOVWload [i1] {s} p mem)))
(ORL x0:(MOVWload [i0] {s} p0 mem)
sh:(SHLLconst [16] x1:(MOVWload [i1] {s} p1 mem)))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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)
sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p mem)))
(ORQ x0:(MOVWload [i0] {s} p0 mem)
sh:(SHLQconst [16] x1:(MOVWload [i1] {s} p1 mem)))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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)
sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p mem)))
(ORQ x0:(MOVLload [i0] {s} p0 mem)
sh:(SHLQconst [32] x1:(MOVLload [i1] {s} p1 mem)))
&& i1 == i0+4
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& clobber(x0, x1, sh)
-> @mergePoint(b,x0,x1) (MOVQload [i0] {s} p mem)
-> @mergePoint(b,x0,x1) (MOVQload [i0] {s} p0 mem)
(ORL
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem))
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p0 mem))
or:(ORL
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem))
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p1 mem))
y))
&& i1 == i0+1
&& j1 == j0+8
@ -1615,14 +1548,15 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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] (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
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem))
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p0 mem))
or:(ORQ
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem))
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p1 mem))
y))
&& i1 == i0+1
&& j1 == j0+8
@ -1632,14 +1566,15 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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] (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
s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p mem))
s1:(SHLQconst [j1] x1:(MOVWload [i1] {s} p0 mem))
or:(ORQ
s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p mem))
s0:(SHLQconst [j0] x0:(MOVWload [i0] {s} p1 mem))
y))
&& i1 == i0+2
&& j1 == j0+16
@ -1649,180 +1584,105 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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] (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
(ORL x0:(MOVBloadidx1 [i0] {s} p idx mem)
sh:(SHLLconst [8] x1:(MOVBloadidx1 [i1] {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) (MOVWloadidx1 <v.Type> [i0] {s} p idx mem)
// Move constants offsets from LEAQx up into load. This lets the above combining
// rules discover indexed load-combining instances.
(MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ1 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ1 <l.Type> [0] {s1} x y) mem)
(MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ2 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ2 <l.Type> [0] {s1} x y) mem)
(MOV(B|W|L|Q)load [i0] {s0} l:(LEAQ4 [i1] {s1} x y) mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)load [i0+i1] {s0} (LEAQ4 <l.Type> [0] {s1} x y) 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)
sh:(SHLQconst [8] x1:(MOVBloadidx1 [i1] {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) (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)
(MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ1 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ1 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ2 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ2 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ4 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ4 <l.Type> [0] {s1} x y) val mem)
(MOV(B|W|L|Q)store [i0] {s0} l:(LEAQ8 [i1] {s1} x y) val mem) && i1 != 0 && is32Bit(i0+i1)
-> (MOV(B|W|L|Q)store [i0+i1] {s0} (LEAQ8 <l.Type> [0] {s1} x y) val mem)
// Big-endian loads
(ORL
x1:(MOVBload [i1] {s} p mem)
sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p mem)))
x1:(MOVBload [i1] {s} p0 mem)
sh:(SHLLconst [8] x0:(MOVBload [i0] {s} p1 mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
x1:(MOVBload [i1] {s} p mem)
sh:(SHLQconst [8] x0:(MOVBload [i0] {s} p mem)))
x1:(MOVBload [i1] {s} p0 mem)
sh:(SHLQconst [8] x0:(MOVBload [i0] {s} p1 mem)))
&& i1 == i0+1
&& x0.Uses == 1
&& x1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem))
sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem))))
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p0 mem))
sh:(SHLLconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p1 mem))))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p mem))
sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p mem))))
r1:(ROLWconst [8] x1:(MOVWload [i1] {s} p0 mem))
sh:(SHLQconst [16] r0:(ROLWconst [8] x0:(MOVWload [i0] {s} p1 mem))))
&& i1 == i0+2
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
r1:(BSWAPL x1:(MOVLload [i1] {s} p mem))
sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p mem))))
r1:(BSWAPL x1:(MOVLload [i1] {s} p0 mem))
sh:(SHLQconst [32] r0:(BSWAPL x0:(MOVLload [i0] {s} p1 mem))))
&& i1 == i0+4
&& x0.Uses == 1
&& x1.Uses == 1
&& r0.Uses == 1
&& r1.Uses == 1
&& sh.Uses == 1
&& same(p0, p1, 1)
&& mergePoint(b,x0,x1) != nil
&& 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
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p mem))
s0:(SHLLconst [j0] x0:(MOVBload [i0] {s} p0 mem))
or:(ORL
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p mem))
s1:(SHLLconst [j1] x1:(MOVBload [i1] {s} p1 mem))
y))
&& i1 == i0+1
&& j1 == j0-8
@ -1832,14 +1692,15 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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] (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
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p mem))
s0:(SHLQconst [j0] x0:(MOVBload [i0] {s} p0 mem))
or:(ORQ
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p mem))
s1:(SHLQconst [j1] x1:(MOVBload [i1] {s} p1 mem))
y))
&& i1 == i0+1
&& j1 == j0-8
@ -1849,14 +1710,15 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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] (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
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
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))
&& i1 == i0+2
&& j1 == j0-16
@ -1868,168 +1730,41 @@
&& s0.Uses == 1
&& s1.Uses == 1
&& or.Uses == 1
&& same(p0, p1, 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> (MOVLload [i0] {s} p 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)
-> @mergePoint(b,x0,x1,y) (ORQ <v.Type> (SHLQconst <v.Type> [j1] (BSWAPL <typ.UInt32> (MOVLload [i0] {s} p0 mem))) y)
// Combine 2 byte stores + shift into rolw 8 + word store
(MOVBstore [i] {s} p w
x0:(MOVBstore [i-1] {s} p (SHRWconst [8] w) mem))
(MOVBstore [i] {s} p1 w
x0:(MOVBstore [i-1] {s} p0 (SHRWconst [8] w) mem))
&& x0.Uses == 1
&& same(p0, p1, 1)
&& clobber(x0)
-> (MOVWstore [i-1] {s} p (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)
-> (MOVWstore [i-1] {s} p0 (ROLWconst <w.Type> [8] w) mem)
// Combine stores + shifts into bswap and larger (unaligned) stores
(MOVBstore [i] {s} p w
x2:(MOVBstore [i-1] {s} p (SHRLconst [8] w)
x1:(MOVBstore [i-2] {s} p (SHRLconst [16] w)
x0:(MOVBstore [i-3] {s} p (SHRLconst [24] w) mem))))
(MOVBstore [i] {s} p3 w
x2:(MOVBstore [i-1] {s} p2 (SHRLconst [8] w)
x1:(MOVBstore [i-2] {s} p1 (SHRLconst [16] w)
x0:(MOVBstore [i-3] {s} p0 (SHRLconst [24] w) mem))))
&& x0.Uses == 1
&& x1.Uses == 1
&& x2.Uses == 1
&& same(p0, p1, 1)
&& same(p1, p2, 1)
&& same(p2, p3, 1)
&& 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
x2:(MOVBstoreidx1 [i-1] {s} p idx (SHRLconst [8] w)
x1:(MOVBstoreidx1 [i-2] {s} p idx (SHRLconst [16] w)
x0:(MOVBstoreidx1 [i-3] {s} p idx (SHRLconst [24] w) mem))))
&& x0.Uses == 1
&& x1.Uses == 1
&& x2.Uses == 1
&& clobber(x0, x1, x2)
-> (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))))))))
(MOVBstore [i] {s} p7 w
x6:(MOVBstore [i-1] {s} p6 (SHRQconst [8] w)
x5:(MOVBstore [i-2] {s} p5 (SHRQconst [16] w)
x4:(MOVBstore [i-3] {s} p4 (SHRQconst [24] w)
x3:(MOVBstore [i-4] {s} p3 (SHRQconst [32] w)
x2:(MOVBstore [i-5] {s} p2 (SHRQconst [40] w)
x1:(MOVBstore [i-6] {s} p1 (SHRQconst [48] w)
x0:(MOVBstore [i-7] {s} p0 (SHRQconst [56] w) mem))))))))
&& x0.Uses == 1
&& x1.Uses == 1
&& x2.Uses == 1
@ -2037,165 +1772,99 @@
&& x4.Uses == 1
&& x5.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)
-> (MOVQstore [i-7] {s} p (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)
-> (MOVQstore [i-7] {s} p0 (BSWAPQ <w.Type> w) mem)
// 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
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVBstoreconst [a] {s} p x:(MOVBstoreconst [c] {s} p mem))
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVBstoreconst [a] {s} p1 x:(MOVBstoreconst [c] {s} p0 mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 1 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [c] {s} p x:(MOVWstoreconst [a] {s} p mem))
-> (MOVWstoreconst [makeValAndOff(ValAndOff(a).Val()&0xff | ValAndOff(c).Val()<<8, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [c] {s} p1 x:(MOVWstoreconst [a] {s} p0 mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVWstoreconst [a] {s} p x:(MOVWstoreconst [c] {s} p mem))
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVWstoreconst [a] {s} p1 x:(MOVWstoreconst [c] {s} p0 mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 2 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p mem)
(MOVLstoreconst [c] {s} p x:(MOVLstoreconst [a] {s} p mem))
-> (MOVLstoreconst [makeValAndOff(ValAndOff(a).Val()&0xffff | ValAndOff(c).Val()<<16, ValAndOff(a).Off())] {s} p0 mem)
(MOVLstoreconst [c] {s} p1 x:(MOVLstoreconst [a] {s} p0 mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVLstoreconst [a] {s} p x:(MOVLstoreconst [c] {s} p mem))
-> (MOVQstore [ValAndOff(a).Off()] {s} p0 (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVLstoreconst [a] {s} p1 x:(MOVLstoreconst [c] {s} p0 mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(a).Off() + 4 == ValAndOff(c).Off()
&& clobber(x)
-> (MOVQstore [ValAndOff(a).Off()] {s} p (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem))
-> (MOVQstore [ValAndOff(a).Off()] {s} p0 (MOVQconst [ValAndOff(a).Val()&0xffffffff | ValAndOff(c).Val()<<32]) mem)
(MOVQstoreconst [c] {s} p1 x:(MOVQstoreconst [c2] {s} p0 mem))
&& config.useSSE
&& x.Uses == 1
&& same(p0, p1, 1)
&& ValAndOff(c2).Off() + 8 == ValAndOff(c).Off()
&& ValAndOff(c).Val() == 0
&& ValAndOff(c2).Val() == 0
&& clobber(x)
-> (MOVOstore [ValAndOff(c2).Off()] {s} p (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)
-> (MOVOstore [ValAndOff(c2).Off()] {s} p0 (MOVOconst [0]) mem)
// 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
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i-1] {s} p w mem)
(MOVBstore [i] {s} p w x:(MOVBstore [i+1] {s} p (SHR(W|L|Q)const [8] w) mem))
-> (MOVWstore [i-1] {s} p0 w mem)
(MOVBstore [i] {s} p1 w x:(MOVBstore [i+1] {s} p0 (SHR(W|L|Q)const [8] w) mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i] {s} p 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))
-> (MOVWstore [i] {s} p0 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
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVWstore [i-1] {s} p w0 mem)
(MOVWstore [i] {s} p (SHR(L|Q)const [16] w) x:(MOVWstore [i-2] {s} p w mem))
-> (MOVWstore [i-1] {s} p0 w0 mem)
(MOVWstore [i] {s} p1 (SHR(L|Q)const [16] w) x:(MOVWstore [i-2] {s} p0 w mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVLstore [i-2] {s} p 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))
-> (MOVLstore [i-2] {s} p0 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
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVLstore [i-2] {s} p w0 mem)
(MOVLstore [i] {s} p (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p w mem))
-> (MOVLstore [i-2] {s} p0 w0 mem)
(MOVLstore [i] {s} p1 (SHRQconst [32] w) x:(MOVLstore [i-4] {s} p0 w mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVQstore [i-4] {s} p w mem)
(MOVLstore [i] {s} p (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p w0:(SHRQconst [j-32] w) mem))
-> (MOVQstore [i-4] {s} p0 w mem)
(MOVLstore [i] {s} p1 (SHRQconst [j] w) x:(MOVLstore [i-4] {s} p0 w0:(SHRQconst [j-32] w) mem))
&& x.Uses == 1
&& same(p0, p1, 1)
&& clobber(x)
-> (MOVQstore [i-4] {s} p 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)
-> (MOVQstore [i-4] {s} p0 w0 mem)
(MOVBstore [i] {s} p
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<<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
// 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).

View File

@ -245,6 +245,7 @@ dd.ssa-prog {
svg {
cursor: default;
outline: 1px solid #eee;
width: 100%;
}
body.darkmode {
@ -975,7 +976,7 @@ func (d *dotWriter) writeFuncSVG(w io.Writer, phase string, f *Func) {
fmt.Println(err)
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)
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"];`)

View File

@ -1305,9 +1305,9 @@ func isNonNegative(v *Value) bool {
if !v.Type.IsInteger() {
panic("isNonNegative bad type")
}
if !v.Type.IsSigned() {
return true
}
// TODO: return true if !v.Type.IsSigned()
// 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 {
case OpConst64:

View File

@ -1247,3 +1247,43 @@ func read64(sym interface{}, off int64, byteorder binary.ByteOrder) uint64 {
copy(buf, src)
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.
func (s *scanner) errorf(format string, args ...interface{}) {
s.bad = true
s.error(fmt.Sprintf(format, args...))
}
// 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{}) {
s.bad = true
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.
//
// 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) {
s.bad = false
ok := true
kind := IntLit
base := 10 // number base
prefix := rune(0) // one of 0 (decimal), '0' (0-octal), 'x', 'o', or 'b'
digsep := 0 // bit 0: digit present, bit 1: '_' present
@ -470,7 +477,6 @@ func (s *scanner) number(seenPoint bool) {
// integer part
if !seenPoint {
s.kind = IntLit
if s.ch == '0' {
s.nextch()
switch lower(s.ch) {
@ -491,7 +497,8 @@ func (s *scanner) number(seenPoint bool) {
digsep |= s.digits(base, &invalid)
if s.ch == '.' {
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()
seenPoint = true
@ -500,68 +507,77 @@ func (s *scanner) number(seenPoint bool) {
// fractional part
if seenPoint {
s.kind = FloatLit
kind = FloatLit
digsep |= s.digits(base, &invalid)
}
if digsep&1 == 0 && !s.bad {
s.errorf("%s has no digits", litname(prefix))
if digsep&1 == 0 && ok {
s.errorf("%s literal has no digits", baseName(base))
ok = false
}
// exponent
if e := lower(s.ch); e == 'e' || e == 'p' {
if !s.bad {
if ok {
switch {
case e == 'e' && prefix != 0 && prefix != '0':
s.errorf("%q exponent requires decimal mantissa", s.ch)
ok = false
case e == 'p' && prefix != 'x':
s.errorf("%q exponent requires hexadecimal mantissa", s.ch)
ok = false
}
}
s.nextch()
s.kind = FloatLit
kind = FloatLit
if s.ch == '+' || s.ch == '-' {
s.nextch()
}
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")
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")
ok = false
}
// suffix 'i'
if s.ch == 'i' {
s.kind = ImagLit
kind = ImagLit
s.nextch()
}
s.nlsemi = true
s.lit = string(s.segment())
s.tok = _Literal
s.setLit(kind, ok) // do this now so we can use s.lit below
if s.kind == IntLit && invalid >= 0 && !s.bad {
s.errorAtf(invalid, "invalid digit %q in %s", s.lit[invalid], litname(prefix))
if kind == IntLit && invalid >= 0 && ok {
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 {
s.errorAtf(i, "'_' must separate successive digits")
ok = false
}
}
s.bad = !ok // correct s.bad
}
func litname(prefix rune) string {
switch prefix {
case 'x':
return "hexadecimal literal"
case 'o', '0':
return "octal literal"
case 'b':
return "binary literal"
func baseName(base int) string {
switch base {
case 2:
return "binary"
case 8:
return "octal"
case 10:
return "decimal"
case 16:
return "hexadecimal"
}
return "decimal literal"
panic("invalid base")
}
// 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() {
s.bad = false
ok := true
s.nextch()
n := 0
for ; ; n++ {
if s.ch == '\'' {
if !s.bad {
if ok {
if n == 0 {
s.errorf("empty rune literal or unescaped '")
ok = false
} else if n != 1 {
s.errorAtf(0, "more than one character in rune literal")
ok = false
}
}
s.nextch()
@ -623,32 +641,33 @@ func (s *scanner) rune() {
}
if s.ch == '\\' {
s.nextch()
s.escape('\'')
if !s.escape('\'') {
ok = false
}
continue
}
if s.ch == '\n' {
if !s.bad {
if ok {
s.errorf("newline in rune literal")
ok = false
}
break
}
if s.ch < 0 {
if !s.bad {
if ok {
s.errorAtf(0, "rune literal not terminated")
ok = false
}
break
}
s.nextch()
}
s.nlsemi = true
s.lit = string(s.segment())
s.kind = RuneLit
s.tok = _Literal
s.setLit(RuneLit, ok)
}
func (s *scanner) stdString() {
s.bad = false
ok := true
s.nextch()
for {
@ -658,28 +677,29 @@ func (s *scanner) stdString() {
}
if s.ch == '\\' {
s.nextch()
s.escape('"')
if !s.escape('"') {
ok = false
}
continue
}
if s.ch == '\n' {
s.errorf("newline in string")
ok = false
break
}
if s.ch < 0 {
s.errorAtf(0, "string not terminated")
ok = false
break
}
s.nextch()
}
s.nlsemi = true
s.lit = string(s.segment())
s.kind = StringLit
s.tok = _Literal
s.setLit(StringLit, ok)
}
func (s *scanner) rawString() {
s.bad = false
ok := true
s.nextch()
for {
@ -689,6 +709,7 @@ func (s *scanner) rawString() {
}
if s.ch < 0 {
s.errorAtf(0, "string not terminated")
ok = false
break
}
s.nextch()
@ -697,10 +718,7 @@ func (s *scanner) rawString() {
// literal (even though they are not part of the literal
// value).
s.nlsemi = true
s.lit = string(s.segment())
s.kind = StringLit
s.tok = _Literal
s.setLit(StringLit, ok)
}
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 base, max uint32
switch s.ch {
case quote, 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\':
s.nextch()
return
return true
case '0', '1', '2', '3', '4', '5', '6', '7':
n, base, max = 3, 8, 255
case 'x':
@ -818,16 +836,16 @@ func (s *scanner) escape(quote rune) {
n, base, max = 8, 16, unicode.MaxRune
default:
if s.ch < 0 {
return // complain in caller about EOF
return true // complain in caller about EOF
}
s.errorf("unknown escape")
return
return false
}
var x uint32
for i := n; i > 0; i-- {
if s.ch < 0 {
return // complain in caller about EOF
return true // complain in caller about EOF
}
d := base
if isDecimal(s.ch) {
@ -836,12 +854,8 @@ func (s *scanner) escape(quote rune) {
d = uint32(lower(s.ch)) - 'a' + 10
}
if d >= base {
kind := "hex"
if base == 8 {
kind = "octal"
}
s.errorf("invalid character %q in %s escape", s.ch, kind)
return
s.errorf("invalid character %q in %s escape", s.ch, baseName(int(base)))
return false
}
// d < base
x = x*base + d
@ -850,10 +864,13 @@ func (s *scanner) escape(quote rune) {
if x > max && base == 8 {
s.errorf("octal escape value %d > 255", x)
return
return false
}
if x > max || 0xD800 <= x && x < 0xE000 /* surrogate range */ {
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},
{`'\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},
{`'\x0'`, "invalid character '\\'' in hex escape", 0, 4},
{`'\x0'`, "invalid character '\\'' in hexadecimal escape", 0, 4},
{`'\00'`, "invalid character '\\'' in octal escape", 0, 4},
{`'\377' /*`, "comment not terminated", 0, 7}, // valid octal escape
{`'\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},
{`"\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},
{`"\x0"`, "invalid character '\"' in hex escape", 0, 4},
{`"\x0"`, "invalid character '\"' in hexadecimal escape", 0, 4},
{`"\00"`, "invalid character '\"' in octal escape", 0, 4},
{`"\377" /*`, "comment not terminated", 0, 7}, // valid octal escape
{`"\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\z00\nbar"`, "unknown escape", 0, 10},
{`"\x`, "string not terminated", 0, 0},
{`"\x"`, "invalid character '\"' in hex escape", 0, 3},
{`var s string = "\x"`, "invalid character '\"' in hex escape", 0, 18},
{`"\x"`, "invalid character '\"' in hexadecimal escape", 0, 3},
{`var s string = "\x"`, "invalid character '\"' in hexadecimal escape", 0, 18},
{`return "\Uffffffff"`, "escape is invalid Unicode code point U+FFFFFFFF", 0, 18},
{"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) {
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)
return nil
}
case *ast.FuncDecl:
// Don't annotate functions with blank names - they cannot be executed.
if n.Name.Name == "_" {
return nil
}
}
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")))
}
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 {
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)))
if flag.NArg() > 0 && t.runRxStr != "" {

View File

@ -3,11 +3,10 @@ module cmd
go 1.14
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/crypto v0.0.0-20200214034016-1d94cc7ab1c6
golang.org/x/mod v0.2.0
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca
)

View File

@ -1,8 +1,9 @@
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/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-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3 h1:SRgJV+IoxM5MKyFdlSUeNy6/ycRUF2yBAKdAQswoHUk=
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/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
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-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
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/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-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-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-20190412213103-97732733099d/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/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-20200219195521-7c4b6277d74d h1:ZQ18He7VORO2x4IEBuwfdp56K+ftEzRjvL0cFuCGCcM=
golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca h1:cFQHQhDv9N1vc+64dtXDAyd3exHDGfRTtveOnD0IsLI=
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-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
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) {
testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
testenv.MustHaveExecPath(t, "svn")
tg := testgo(t)
defer tg.cleanup()
@ -2208,15 +2209,10 @@ func testBuildmodePIE(t *testing.T, useCgo bool) {
t.Fatal(err)
}
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 {
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")
}
var dc uint16
@ -2225,13 +2221,13 @@ func testBuildmodePIE(t *testing.T, useCgo bool) {
dc = oh.DllCharacteristics
case *pe.OptionalHeader64:
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")
}
default:
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")
}
default:

View File

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

View File

@ -13,19 +13,19 @@
// or an F_OFD_SETLK command for 'fcntl', that allows for better concurrency and
// does not require per-inode bookkeeping in the application.
//
// TODO(bcmills): If we add a build tag for Illumos (see golang.org/issue/20603)
// then Illumos should use F_OFD_SETLK, and the resulting code would be as
// 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.
// TODO(golang.org/issue/35618): add a syscall.Flock binding for Illumos and
// switch it over to use filelock_unix.go.
package filelock
import (
"errors"
"io"
"math/rand"
"os"
"sync"
"syscall"
"time"
)
type lockType int16
@ -91,7 +91,67 @@ func lock(f File, lt lockType) (err error) {
wait <- f
}
err = setlkw(f.Fd(), lt)
// 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)
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 {
unlock(f)

View File

@ -8,8 +8,11 @@
package lockedfile_test
import (
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
@ -172,3 +175,98 @@ func TestCanLockExistingFile(t *testing.T) {
f.Close()
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 (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
@ -67,12 +68,10 @@ func verifyMod(mod module.Version) bool {
_, zipErr = os.Stat(zip)
}
dir, dirErr := modfetch.DownloadDir(mod)
if dirErr == nil {
_, dirErr = os.Stat(dir)
}
data, err := ioutil.ReadFile(zip + "hash")
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.
return true
}
@ -81,7 +80,7 @@ func verifyMod(mod module.Version) bool {
}
h := string(bytes.TrimSpace(data))
if zipErr != nil && os.IsNotExist(zipErr) {
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) {
// ok
} else {
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
@ -93,7 +92,7 @@ func verifyMod(mod module.Version) bool {
ok = false
}
}
if dirErr != nil && os.IsNotExist(dirErr) {
if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
// ok
} else {
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)

View File

@ -7,6 +7,7 @@ package modfetch
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -56,8 +57,11 @@ func CachePath(m module.Version, suffix string) (string, error) {
return filepath.Join(dir, encVer+"."+suffix), nil
}
// DownloadDir returns the directory to which m should be downloaded.
// Note that the directory may not yet exist.
// DownloadDir returns the directory to which m should have been downloaded.
// 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) {
if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
@ -76,9 +80,39 @@ func DownloadDir(m module.Version) (string, error) {
if err != nil {
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
// and extraction of the zipfile for the given module version.
func lockVersion(mod module.Version) (unlock func(), err error) {

View File

@ -563,7 +563,7 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
return err
}
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 := ""

View File

@ -46,24 +46,26 @@ func Download(mod module.Version) (dir string, err error) {
err error
}
c := downloadCache.Do(mod, func() interface{} {
dir, err := DownloadDir(mod)
dir, err := download(mod)
if err != nil {
return cached{"", err}
}
if err := download(mod, dir); err != nil {
return cached{"", err}
}
checkMod(mod)
return cached{dir, nil}
}).(cached)
return c.dir, c.err
}
func download(mod module.Version, dir string) (err error) {
// If the directory exists, the module has already been extracted.
fi, err := os.Stat(dir)
if err == nil && fi.IsDir() {
return nil
func download(mod module.Version) (dir string, err error) {
// If the directory exists, and no .partial file exists, the module has
// already been completely extracted. .partial files may be created when a
// module zip directory is extracted in place instead of being extracted to a
// 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,
@ -71,22 +73,24 @@ func download(mod module.Version, dir string) (err error) {
// Invoke DownloadZip before locking the file.
zipfile, err := DownloadZip(mod)
if err != nil {
return err
return "", err
}
unlock, err := lockVersion(mod)
if err != nil {
return err
return "", err
}
defer unlock()
// Check whether the directory was populated while we were waiting on the lock.
fi, err = os.Stat(dir)
if err == nil && fi.IsDir() {
return nil
_, dirErr := DownloadDir(mod)
if dirErr == 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
// are no longer active.
parentDir := filepath.Dir(dir)
@ -96,35 +100,75 @@ func download(mod module.Version, dir string) (err error) {
RemoveAll(path) // best effort
}
}
// Extract the zip file to a temporary directory, then rename it to the
// final path. That way, we can use the existence of the source directory to
// 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
}
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
if err != nil {
return err
}
defer func() {
if err != nil {
RemoveAll(tmpDir)
if dirExists {
if err := RemoveAll(dir); err != nil {
return "", err
}
}()
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
return err
}
if err := robustio.Rename(tmpDir, dir); 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)
if err != nil {
return "", err
}
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
RemoveAll(tmpDir)
return "", err
}
if err := robustio.Rename(tmpDir, dir); err != nil {
RemoveAll(tmpDir)
return "", err
}
}
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.
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
@ -306,7 +361,7 @@ func RemoveAll(dir string) error {
}
return nil
})
return os.RemoveAll(dir)
return robustio.RemoveAll(dir)
}
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-]+)*)?$`)
const pseudoVersionTimestampFormat = "20060102150405"
// 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,
// 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 == "" {
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)
older = semver.Canonical(older)
if older == "" {

View File

@ -148,9 +148,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
}
dir, err := modfetch.DownloadDir(mod)
if err == nil {
if info, err := os.Stat(dir); err == nil && info.IsDir() {
m.Dir = dir
}
m.Dir = dir
}
}
}

View File

@ -59,27 +59,6 @@ var (
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.
//
// 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() {
if modFile == nil {
path, err := findModulePath(modRoot)
@ -983,113 +867,3 @@ func WriteGoMod() {
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"
"fmt"
"go/build"
"io/ioutil"
"os"
"path"
pathpkg "path"
"path/filepath"
"sort"
"strings"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
@ -27,9 +25,7 @@ import (
"cmd/go/internal/search"
"cmd/go/internal/str"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// buildList is the list of modules to use for building packages.
@ -1033,354 +1029,3 @@ func WhyDepth(path string) int {
}
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"
)
const arbitraryTimeout = 500 * time.Millisecond
const arbitraryTimeout = 2000 * time.Millisecond
// retry retries ephemeral errors from f up to an arbitrary timeout
// 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 {
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, "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)

View File

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

View File

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

View File

@ -7,7 +7,8 @@ env GO111MODULE=off
[!linux] skip # test only works if c-archive implies -shared
[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'
-- override.go --

View File

@ -1,15 +1,16 @@
[short] 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".*"-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[^"]+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 --
package main
// #cgo CFLAGS: -I"c flags"
// #cgo LDFLAGS: -L"ld flags"
import "C"
func main() {}
func 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'
[short] skip
env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
# -gcflags=-e applies to named packages, not dependencies
go build -n -v -gcflags=-e z1 z2
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
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 --
module m
go 1.8

View File

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

View File

@ -14,7 +14,8 @@ stdout 'rsc.io/quote v1.5.2'
[short] skip
# 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 example.com/version '
! 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
cd outside
! 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 ..
! 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
# with a valid one.
go mod edit -replace golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c=golang.org/x/text@14c0d48ead0c
cd outside
! 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 ..
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'

View File

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

View File

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

View File

@ -532,22 +532,16 @@ var Anames = []string{
"STXVW4X",
"STXVH8X",
"STXVB16X",
"LXS",
"LXSDX",
"STXS",
"STXSDX",
"LXSI",
"LXSIWAX",
"LXSIWZX",
"STXSI",
"STXSIWX",
"MFVSR",
"MFVSRD",
"MFFPRD",
"MFVRD",
"MFVSRWZ",
"MFVSRLD",
"MTVSR",
"MTVSRD",
"MTFPRD",
"MTVRD",
@ -556,7 +550,6 @@ var Anames = []string{
"MTVSRDD",
"MTVSRWS",
"XXLAND",
"XXLANDQ",
"XXLANDC",
"XXLEQV",
"XXLNAND",
@ -566,34 +559,27 @@ var Anames = []string{
"XXLORQ",
"XXLXOR",
"XXSEL",
"XXMRG",
"XXMRGHW",
"XXMRGLW",
"XXSPLT",
"XXSPLTW",
"XXPERM",
"XXPERMDI",
"XXSI",
"XXSLDWI",
"XSCV",
"XSCVDPSP",
"XSCVSPDP",
"XSCVDPSPN",
"XSCVSPDPN",
"XVCV",
"XVCVDPSP",
"XVCVSPDP",
"XSCVX",
"XSCVDPSXDS",
"XSCVDPSXWS",
"XSCVDPUXDS",
"XSCVDPUXWS",
"XSCVXP",
"XSCVSXDDP",
"XSCVUXDDP",
"XSCVSXDSP",
"XSCVUXDSP",
"XVCVX",
"XVCVDPSXDS",
"XVCVDPSXWS",
"XVCVDPUXDS",
@ -602,7 +588,6 @@ var Anames = []string{
"XVCVSPSXWS",
"XVCVSPUXDS",
"XVCVSPUXWS",
"XVCVXP",
"XVCVSXDDP",
"XVCVSXWDP",
"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 */
/* Vector splat */
{AVSPLT, 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},
{AVSPLTI, 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},
{AVSPLTB, C_SCON, C_VREG, C_NONE, C_VREG, 82, 4, 0}, /* vector splat, vx-form */
{AVSPLTB, C_ADDCON, C_VREG, C_NONE, C_VREG, 82, 4, 0},
{AVSPLTISB, C_SCON, C_NONE, C_NONE, C_VREG, 82, 4, 0}, /* vector splat immediate, vx-form */
{AVSPLTISB, C_ADDCON, C_NONE, C_NONE, C_VREG, 82, 4, 0},
/* Vector AES */
{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 */
/* 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 */
{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 */
{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 */
{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 */
{AMFVSR, 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},
{AMFVSR, C_VREG, C_NONE, C_NONE, C_REG, 88, 4, 0},
{AMFVSRD, C_VSREG, C_NONE, C_NONE, C_REG, 88, 4, 0}, /* vsx move from vsr, xx1-form */
{AMFVSRD, C_FREG, 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 */
{AMTVSR, 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},
{AMTVSR, 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_VSREG, 88, 4, 0}, /* vsx move to vsr, xx1-form */
{AMTVSRD, C_REG, C_REG, C_NONE, C_VSREG, 88, 4, 0},
{AMTVSRD, C_REG, C_NONE, C_NONE, C_FREG, 88, 4, 0},
{AMTVSRD, C_REG, C_NONE, C_NONE, C_VREG, 88, 4, 0},
/* VSX logical */
{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 */
/* 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 */
{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 */
{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 */
{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 */
{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 */
{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 */
{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 */
{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 */
{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 */
{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 */
{AMOVD, C_REG, C_NONE, C_NONE, C_SPR, 66, 4, 0},
@ -1519,13 +1519,11 @@ func buildop(ctxt *obj.Link) {
case AVSEL: /* vsel */
opset(AVSEL, r0)
case AVSPLT: /* vspltb, vsplth, vspltw */
opset(AVSPLTB, r0)
case AVSPLTB: /* vspltb, vsplth, vspltw */
opset(AVSPLTH, r0)
opset(AVSPLTW, r0)
case AVSPLTI: /* vspltisb, vspltish, vspltisw */
opset(AVSPLTISB, r0)
case AVSPLTISB: /* vspltisb, vspltish, vspltisw */
opset(AVSPLTISH, r0)
opset(AVSPLTISW, r0)
@ -1561,28 +1559,25 @@ func buildop(ctxt *obj.Link) {
case ASTXV: /* stxv */
opset(ASTXV, r0)
case ALXS: /* lxsdx */
case ALXSDX: /* lxsdx */
opset(ALXSDX, r0)
case ASTXS: /* stxsdx */
case ASTXSDX: /* stxsdx */
opset(ASTXSDX, r0)
case ALXSI: /* lxsiwax, lxsiwzx */
opset(ALXSIWAX, r0)
case ALXSIWAX: /* lxsiwax, lxsiwzx */
opset(ALXSIWZX, r0)
case ASTXSI: /* stxsiwx */
case ASTXSIWX: /* stxsiwx */
opset(ASTXSIWX, r0)
case AMFVSR: /* mfvsrd, mfvsrwz (and extended mnemonics), mfvsrld */
opset(AMFVSRD, r0)
case AMFVSRD: /* mfvsrd, mfvsrwz (and extended mnemonics), mfvsrld */
opset(AMFFPRD, r0)
opset(AMFVRD, r0)
opset(AMFVSRWZ, r0)
opset(AMFVSRLD, r0)
case AMTVSR: /* mtvsrd, mtvsrwa, mtvsrwz (and extended mnemonics), mtvsrdd, mtvsrws */
opset(AMTVSRD, r0)
case AMTVSRD: /* mtvsrd, mtvsrwa, mtvsrwz (and extended mnemonics), mtvsrdd, mtvsrws */
opset(AMTFPRD, r0)
opset(AMTVRD, r0)
opset(AMTVSRWA, r0)
@ -1591,7 +1586,6 @@ func buildop(ctxt *obj.Link) {
opset(AMTVSRWS, r0)
case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */
opset(AXXLANDQ, r0)
opset(AXXLANDC, r0)
opset(AXXLEQV, r0)
opset(AXXLNAND, r0)
@ -1605,42 +1599,38 @@ func buildop(ctxt *obj.Link) {
case AXXSEL: /* xxsel */
opset(AXXSEL, r0)
case AXXMRG: /* xxmrghw, xxmrglw */
opset(AXXMRGHW, r0)
case AXXMRGHW: /* xxmrghw, xxmrglw */
opset(AXXMRGLW, r0)
case AXXSPLT: /* xxspltw */
case AXXSPLTW: /* xxspltw */
opset(AXXSPLTW, r0)
case AXXPERM: /* xxpermdi */
opset(AXXPERMDI, r0)
opset(AXXPERM, r0)
case AXXSI: /* xxsldwi */
case AXXSLDWI: /* xxsldwi */
opset(AXXPERMDI, r0)
opset(AXXSLDWI, r0)
case AXSCV: /* xscvdpsp, xscvspdp, xscvdpspn, xscvspdpn */
opset(AXSCVDPSP, r0)
case AXSCVDPSP: /* xscvdpsp, xscvspdp, xscvdpspn, xscvspdpn */
opset(AXSCVSPDP, r0)
opset(AXSCVDPSPN, r0)
opset(AXSCVSPDPN, r0)
case AXVCV: /* xvcvdpsp, xvcvspdp */
opset(AXVCVDPSP, r0)
case AXVCVDPSP: /* xvcvdpsp, xvcvspdp */
opset(AXVCVSPDP, r0)
case AXSCVX: /* xscvdpsxds, xscvdpsxws, xscvdpuxds, xscvdpuxws */
opset(AXSCVDPSXDS, r0)
case AXSCVDPSXDS: /* xscvdpsxds, xscvdpsxws, xscvdpuxds, xscvdpuxws */
opset(AXSCVDPSXWS, r0)
opset(AXSCVDPUXDS, r0)
opset(AXSCVDPUXWS, r0)
case AXSCVXP: /* xscvsxddp, xscvuxddp, xscvsxdsp, xscvuxdsp */
opset(AXSCVSXDDP, r0)
case AXSCVSXDDP: /* xscvsxddp, xscvuxddp, xscvsxdsp, xscvuxdsp */
opset(AXSCVUXDDP, r0)
opset(AXSCVSXDSP, 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(AXVCVDPSXWS, r0)
opset(AXVCVDPUXDS, r0)
@ -1650,8 +1640,7 @@ func buildop(ctxt *obj.Link) {
opset(AXVCVSPUXDS, r0)
opset(AXVCVSPUXWS, r0)
case AXVCVXP: /* xvcvsxddp, xvcvsxwdp, xvcvuxddp, xvcvuxwdp, xvcvsxdsp, xvcvsxwsp, xvcvuxdsp, xvcvuxwsp */
opset(AXVCVSXDDP, r0)
case AXVCVSXDDP: /* xvcvsxddp, xvcvsxwdp, xvcvuxddp, xvcvuxwdp, xvcvsxdsp, xvcvsxwsp, xvcvuxdsp, xvcvuxwsp */
opset(AXVCVSXWDP, r0)
opset(AXVCVUXDDP, r0)
opset(AXVCVUXWDP, r0)
@ -4616,7 +4605,7 @@ func (c *ctxt9) oprrr(a obj.As) uint32 {
case AMTVSRWS:
return OPVXX1(31, 403, 0) /* mtvsrws - v3.00 */
case AXXLANDQ:
case AXXLAND:
return OPVXX3(60, 130, 0) /* xxland - v2.06 */
case AXXLANDC:
return OPVXX3(60, 138, 0) /* xxlandc - v2.06 */
@ -4645,6 +4634,8 @@ func (c *ctxt9) oprrr(a obj.As) uint32 {
case AXXSPLTW:
return OPVXX2(60, 164, 0) /* xxspltw - v2.06 */
case AXXPERM:
return OPVXX3(60, 26, 0) /* xxperm - v2.06 */
case AXXPERMDI:
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)
}
// 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
}

View File

@ -1239,6 +1239,7 @@ func TestPackageNameAttr(t *testing.T) {
}
rdr := d.Reader()
runtimeUnitSeen := false
for {
e, err := rdr.Next()
if err != nil {
@ -1254,11 +1255,25 @@ func TestPackageNameAttr(t *testing.T) {
continue
}
_, ok := e.Val(dwarfAttrGoPackageName).(string)
pn, ok := e.Val(dwarfAttrGoPackageName).(string)
if !ok {
name, _ := e.Val(dwarf.AttrName).(string)
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,47 +58,15 @@ var (
)
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_INITIALIZED_DATA = 0x00000040
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
IMAGE_SCN_MEM_EXECUTE = 0x20000000
IMAGE_SCN_MEM_READ = 0x40000000
IMAGE_SCN_MEM_WRITE = 0x80000000
IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
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
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
IMAGE_SCN_MEM_EXECUTE = 0x20000000
IMAGE_SCN_MEM_READ = 0x40000000
IMAGE_SCN_MEM_WRITE = 0x80000000
IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
IMAGE_SCN_ALIGN_32BYTES = 0x600000
)
// TODO(crawshaw): add these constants to debug/pe.
@ -762,11 +730,11 @@ func (f *peFile) writeFileHeader(ctxt *Link) {
default:
Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
case sys.AMD64:
fh.Machine = IMAGE_FILE_MACHINE_AMD64
fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64
case sys.I386:
fh.Machine = IMAGE_FILE_MACHINE_I386
fh.Machine = pe.IMAGE_FILE_MACHINE_I386
case sys.ARM:
fh.Machine = IMAGE_FILE_MACHINE_ARMNT
fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT
}
fh.NumberOfSections = uint16(len(f.sections))
@ -776,24 +744,24 @@ func (f *peFile) writeFileHeader(ctxt *Link) {
fh.TimeDateStamp = 0
if ctxt.LinkMode == LinkExternal {
fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED
fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED
} 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 {
case sys.AMD64, sys.I386:
if ctxt.BuildMode != BuildModePIE {
fh.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED
fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED
}
}
}
if pe64 != 0 {
var oh64 pe.OptionalHeader64
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE
fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE
} else {
var oh pe.OptionalHeader32
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE
fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE
}
fh.PointerToSymbolTable = uint32(f.symtabOffset)
@ -854,36 +822,36 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) {
oh64.SizeOfHeaders = uint32(PEFILEHEADR)
oh.SizeOfHeaders = uint32(PEFILEHEADR)
if windowsgui {
oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI
} else {
oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI
}
// Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
// Enable DEP
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT
oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT
// The DLL can be relocated at load time.
switch ctxt.Arch.Family {
case sys.AMD64, sys.I386:
if ctxt.BuildMode == BuildModePIE {
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
}
case sys.ARM:
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
}
// Image can handle a high entropy 64-bit virtual address space.
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
@ -1229,10 +1197,10 @@ func addimports(ctxt *Link, datsect *peSection) {
out.Write32(0)
// update data directory
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
pefile.dataDirectory[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_IMPORT].VirtualAddress = isect.virtualAddress
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)
out.SeekSet(endoff)
}
@ -1272,8 +1240,8 @@ func addexports(ctxt *Link) {
sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
sect.checkOffset(ctxt.Out.Offset())
va := int(sect.virtualAddress)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
vaName := va + binary.Size(&e) + nexport*4
vaAddr := va + binary.Size(&e)
@ -1481,8 +1449,8 @@ func addPEBaseReloc(ctxt *Link) {
rsect.checkOffset(startoff)
rsect.pad(ctxt.Out, uint32(size))
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
}
func (ctxt *Link) dope() {
@ -1528,9 +1496,9 @@ func addpersrc(ctxt *Link) {
h.pad(ctxt.Out, uint32(size))
// 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) {

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))
args := []string{"build", "-o", hello}
args = append(args, flags...)
args = append(args, "testdata/fmthello.go")
out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
args = append(args, "fmthello.go")
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 {
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...)
}
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 {
t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
}

View File

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

View File

@ -834,10 +834,19 @@ func printTraces(w io.Writer, rpt *Report) error {
_, locations := graph.CreateNodes(prof, &graph.Options{})
for _, sample := range prof.Sample {
var stack graph.Nodes
type stk struct {
*graph.NodeInfo
inline bool
}
var stack []stk
for _, loc := range sample.Location {
id := loc.ID
stack = append(stack, locations[id]...)
nodes := locations[loc.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 {
@ -875,10 +884,15 @@ func printTraces(w io.Writer, rpt *Report) error {
if d != 0 {
v = v / d
}
fmt.Fprintf(w, "%10s %s\n",
rpt.formatValue(v), stack[0].Info.PrintableName())
for _, s := range stack[1:] {
fmt.Fprintf(w, "%10s %s\n", "", s.Info.PrintableName())
for i, s := range stack {
var vs, inline string
if i == 0 {
vs = rpt.formatValue(v)
}
if s.inline {
inline = " (inline)"
}
fmt.Fprintf(w, "%10s %s%s\n", vs, s.PrintableName(), inline)
}
}
fmt.Fprintln(w, separator)

View File

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

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
github.com/google/pprof/driver
github.com/google/pprof/internal/binutils
@ -43,7 +43,7 @@ golang.org/x/mod/zip
## explicit
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d
# golang.org/x/tools v0.0.0-20200309180859-aa4048aca1ca
## explicit
golang.org/x/tools/go/analysis
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/errorsas
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/internal/analysisutil
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/shift
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/tests
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/typeutil
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
## explicit
golang.org/x/xerrors
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/errorsas"
"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/lostcancel"
"golang.org/x/tools/go/analysis/passes/nilfunc"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/passes/shift"
"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/tests"
"golang.org/x/tools/go/analysis/passes/unmarshal"
@ -43,12 +45,14 @@ func main() {
copylock.Analyzer,
errorsas.Analyzer,
httpresponse.Analyzer,
ifaceassert.Analyzer,
loopclosure.Analyzer,
lostcancel.Analyzer,
nilfunc.Analyzer,
printf.Analyzer,
shift.Analyzer,
stdmethods.Analyzer,
stringintconv.Analyzer,
structtag.Analyzer,
tests.Analyzer,
unmarshal.Analyzer,

View File

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

View File

@ -62,7 +62,7 @@ func TestSignatureSelection(t *testing.T) {
t.Errorf("test[%d]: unexpected selectSignatureScheme error: %v", testNo, err)
}
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)
if err != nil {
@ -115,7 +115,7 @@ func TestSignatureSelection(t *testing.T) {
for testNo, test := range badTests {
sigAlg, err := selectSignatureScheme(test.tlsVersion, test.cert, test.peerSigAlgs)
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)
}
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())
@ -140,7 +140,7 @@ func TestLegacyTypeAndHash(t *testing.T) {
t.Errorf("ECDSA: expected signature type %#x, got %#x", expectedSigType, sigType)
}
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.
@ -156,13 +156,13 @@ func TestSupportedSignatureAlgorithms(t *testing.T) {
for _, sigAlg := range supportedSignatureAlgorithms {
sigType, hash, err := typeAndHashFromSignatureScheme(sigAlg)
if err != nil {
t.Errorf("%#04x: unexpected error: %v", sigAlg, err)
t.Errorf("%v: unexpected error: %v", sigAlg, err)
}
if sigType == 0 {
t.Errorf("%#04x: missing signature type", sigAlg)
t.Errorf("%v: missing signature type", sigAlg)
}
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)
}
//go:generate stringer -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go
// SignatureScheme identifies a signature algorithm supported by TLS. See
// RFC 8446, Section 4.2.3.
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()
const _IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
switch oh := f.OptionalHeader.(type) {
case *OptionalHeader32:
if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI {
t.Errorf("unexpected Subsystem value: have %d, but want %d", 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)
}
case *OptionalHeader64:
if oh.Subsystem != _IMAGE_SUBSYSTEM_WINDOWS_GUI {
t.Errorf("unexpected Subsystem value: have %d, but want %d", 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)
}
default:
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_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.
//
// 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
// error, or to any interface type.

View File

@ -4,7 +4,7 @@ go 1.14
require (
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/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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20200219183655-46282727080f/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.
// If MaxLen is not 0, make sure MaxLen >= 4.
var MaxLen int
// 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.
// Requires 2 <= len(b) <= MaxLen.
func IndexString(a, b string) int {
panic("unimplemented")
func IndexString(s, substr string) int {
// 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

View File

@ -412,10 +412,10 @@ func NewFile(r io.ReaderAt) (*File, error) {
sect.Relocs[i].Type = rel.Rtype
sect.Relocs[i].Length = rel.Rsize&0x3F + 1
if rel.Rsize&0x80 == 1 {
if rel.Rsize&0x80 != 0 {
sect.Relocs[i].Signed = true
}
if rel.Rsize&0x40 == 1 {
if rel.Rsize&0x40 != 0 {
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].Type = rel.Rtype
sect.Relocs[i].Length = rel.Rsize&0x3F + 1
if rel.Rsize&0x80 == 1 {
if rel.Rsize&0x80 != 0 {
sect.Relocs[i].Signed = true
}
if rel.Rsize&0x40 == 1 {
if rel.Rsize&0x40 != 0 {
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
// any error encountered. If ReadByte returns an error, no input
// 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 {
ReadByte() (byte, error)
}

View File

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

View File

@ -441,6 +441,14 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
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
wg.Add(2)
handler := func(dss *dualStackServer, ln Listener) {
@ -450,7 +458,7 @@ func TestDialParallelSpuriousConnection(t *testing.T) {
t.Fatal(err)
}
// The client should close itself, without sending data.
c.SetReadDeadline(time.Now().Add(1 * time.Second))
c.SetReadDeadline(readDeadline)
var b [1]byte
if _, err := c.Read(b[:]); 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)
}
// TODO: cookies. parsing them isn't exported, though.
uriStr := params["REQUEST_URI"]
if uriStr == "" {
// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.

View File

@ -4135,10 +4135,19 @@ func TestServerConnState(t *testing.T) {
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 {
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:
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
// (called from interhash or nilinterhash, above) or for hashing in
// 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 {
if t.tflag&tflagRegularMemory != 0 {
// Handle ptr sizes specially, see issue 37086.
@ -195,12 +197,28 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
return h
case kindStruct:
s := (*structtype)(unsafe.Pointer(t))
memStart := uintptr(0)
memEnd := uintptr(0)
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() {
continue
}
h = typehash(f.typ, add(p, f.offset()), h)
if f.typ.tflag&tflagRegularMemory == 0 {
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
default:

View File

@ -10,18 +10,18 @@ func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
// Check that (*[n]elem)(p) is appropriately aligned.
// TODO(mdempsky): What about fieldAlign?
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.
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) {
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
@ -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

View File

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

View File

@ -15,6 +15,7 @@ import (
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"syscall"
"testing"
@ -288,6 +289,12 @@ func TestSignalExitStatus(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")
want := "OK\n"
if output != want {

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