From 0406d3a8e5301bd5fd697018e6e8fbb9c75eeb42 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 8 Sep 2021 15:51:10 -0400 Subject: go/ast: rename MultiIndexExpr to IndexListExpr As discussed in #47781, IndexListExpr is one character shorter and has the advantage of being next to IndexExpr in documentation. Updates #47781 Change-Id: I709d5c1a79b4f9aebcd6445e4ab0cd6dae45bab7 Reviewed-on: https://go-review.googlesource.com/c/go/+/348609 Trust: Robert Findley Run-TryBot: Robert Findley Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- src/go/ast/ast.go | 10 +++++----- src/go/ast/walk.go | 2 +- src/go/internal/typeparams/typeparams.go | 13 ++++++------- src/go/parser/parser.go | 4 ++-- src/go/printer/nodes.go | 2 +- src/go/types/call.go | 2 +- src/go/types/expr.go | 2 +- src/go/types/exprstring.go | 2 +- src/go/types/resolver.go | 2 +- src/go/types/signature.go | 2 +- src/go/types/struct.go | 2 +- src/go/types/typexpr.go | 2 +- 12 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index f9223e4f91..70d0912f67 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -344,9 +344,9 @@ type ( Rbrack token.Pos // position of "]" } - // A MultiIndexExpr node represents an expression followed by multiple + // An IndexListExpr node represents an expression followed by multiple // indices. - MultiIndexExpr struct { + IndexListExpr struct { X Expr // expression Lbrack token.Pos // position of "[" Indices []Expr // index expressions @@ -496,7 +496,7 @@ func (x *CompositeLit) Pos() token.Pos { func (x *ParenExpr) Pos() token.Pos { return x.Lparen } func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() } func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() } -func (x *MultiIndexExpr) Pos() token.Pos { return x.X.Pos() } +func (x *IndexListExpr) Pos() token.Pos { return x.X.Pos() } func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() } func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() } func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() } @@ -530,7 +530,7 @@ func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 } func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 } func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 } -func (x *MultiIndexExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *IndexListExpr) End() token.Pos { return x.Rbrack + 1 } func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 } func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 } func (x *CallExpr) End() token.Pos { return x.Rparen + 1 } @@ -562,7 +562,7 @@ func (*CompositeLit) exprNode() {} func (*ParenExpr) exprNode() {} func (*SelectorExpr) exprNode() {} func (*IndexExpr) exprNode() {} -func (*MultiIndexExpr) exprNode() {} +func (*IndexListExpr) exprNode() {} func (*SliceExpr) exprNode() {} func (*TypeAssertExpr) exprNode() {} func (*CallExpr) exprNode() {} diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go index 530735e76f..308662f633 100644 --- a/src/go/ast/walk.go +++ b/src/go/ast/walk.go @@ -116,7 +116,7 @@ func Walk(v Visitor, node Node) { Walk(v, n.X) Walk(v, n.Index) - case *MultiIndexExpr: + case *IndexListExpr: Walk(v, n.X) for _, index := range n.Indices { Walk(v, index) diff --git a/src/go/internal/typeparams/typeparams.go b/src/go/internal/typeparams/typeparams.go index 9bf4f7bf97..3f84f2f0d0 100644 --- a/src/go/internal/typeparams/typeparams.go +++ b/src/go/internal/typeparams/typeparams.go @@ -21,7 +21,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token. Rbrack: rbrack, } default: - return &ast.MultiIndexExpr{ + return &ast.IndexListExpr{ X: x, Lbrack: lbrack, Indices: exprs, @@ -30,25 +30,24 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token. } } -// IndexExpr wraps an ast.IndexExpr or ast.MultiIndexExpr into the -// MultiIndexExpr interface. +// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr. // // Orig holds the original ast.Expr from which this IndexExpr was derived. type IndexExpr struct { - Orig ast.Expr // the wrapped expr, which may be distinct from MultiIndexExpr below. - *ast.MultiIndexExpr + Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below. + *ast.IndexListExpr } func UnpackIndexExpr(n ast.Node) *IndexExpr { switch e := n.(type) { case *ast.IndexExpr: - return &IndexExpr{e, &ast.MultiIndexExpr{ + return &IndexExpr{e, &ast.IndexListExpr{ X: e.X, Lbrack: e.Lbrack, Indices: []ast.Expr{e.Index}, Rbrack: e.Rbrack, }} - case *ast.MultiIndexExpr: + case *ast.IndexListExpr: return &IndexExpr{e, e} } return nil diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 5c0af8d3b8..049515656c 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1570,7 +1570,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr { panic("unreachable") case *ast.SelectorExpr: case *ast.IndexExpr: - case *ast.MultiIndexExpr: + case *ast.IndexListExpr: case *ast.SliceExpr: case *ast.TypeAssertExpr: // If t.Type == nil we have a type assertion of the form @@ -1660,7 +1660,7 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) { return } // x is possibly a composite literal type - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: if p.exprLev < 0 { return } diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 9ce0115426..053a8ef174 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -873,7 +873,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { p.expr0(x.Index, depth+1) p.print(x.Rbrack, token.RBRACK) - case *ast.MultiIndexExpr: + case *ast.IndexListExpr: // TODO(gri): as for IndexExpr, should treat [] like parentheses and undo // one level of depth p.expr1(x.X, token.HighestPrec, 1) diff --git a/src/go/types/call.go b/src/go/types/call.go index 39cd67c5f3..3710756c29 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -337,7 +337,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type if sig.TypeParams().Len() > 0 { if !check.allowVersion(check.pkg, 1, 18) { switch call.Fun.(type) { - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(call.Fun) check.softErrorf(inNode(call.Fun, ix.Lbrack), _Todo, "function instantiation requires go1.18 or later") default: diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 5ca4edebcb..007205a9fb 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1392,7 +1392,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { case *ast.SelectorExpr: check.selector(x, e) - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(e) if check.indexExpr(x, ix) { check.funcInst(x, ix) diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go index aee8a5ba5f..06e7a9dcb4 100644 --- a/src/go/types/exprstring.go +++ b/src/go/types/exprstring.go @@ -67,7 +67,7 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { buf.WriteByte('.') buf.WriteString(x.Sel.Name) - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(x) WriteExpr(buf, ix.X) buf.WriteByte('[') diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index b04a673ab7..486c09220b 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -513,7 +513,7 @@ L: // unpack receiver type // unpack type parameters, if any switch rtyp.(type) { - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(rtyp) rtyp = ix.X if unpackParams { diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 0561947901..88ea07d5d3 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -326,7 +326,7 @@ func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr { new.X = X return &new } - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(x) var newIndexes []ast.Expr for i, index := range ix.Indices { diff --git a/src/go/types/struct.go b/src/go/types/struct.go index f6e6f2a5e6..24a2435ff7 100644 --- a/src/go/types/struct.go +++ b/src/go/types/struct.go @@ -176,7 +176,7 @@ func embeddedFieldIdent(e ast.Expr) *ast.Ident { return e.Sel case *ast.IndexExpr: return embeddedFieldIdent(e.X) - case *ast.MultiIndexExpr: + case *ast.IndexListExpr: return embeddedFieldIdent(e.X) } return nil // invalid embedded field diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index af56297144..6b4a3538b6 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -258,7 +258,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { check.errorf(&x, _NotAType, "%s is not a type", &x) } - case *ast.IndexExpr, *ast.MultiIndexExpr: + case *ast.IndexExpr, *ast.IndexListExpr: ix := typeparams.UnpackIndexExpr(e) if !check.allowVersion(check.pkg, 1, 18) { check.softErrorf(inNode(e, ix.Lbrack), _Todo, "type instantiation requires go1.18 or later") -- cgit v1.2.3-54-g00ecf From 30e9bfbcefb9492d66bd56ea7df6d6426ae8a711 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 13:30:36 -0700 Subject: cmd/compile/internal/types2: implement deduplication of instances using the Environment This is a port of CL 344390 with adjustments to names to make it work for types2. Change-Id: I05c33d9858f973adfbf48d8a1faaf377280f6985 Reviewed-on: https://go-review.googlesource.com/c/go/+/348572 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/noder/reader2.go | 4 +- src/cmd/compile/internal/types2/check.go | 4 +- src/cmd/compile/internal/types2/decl.go | 2 +- src/cmd/compile/internal/types2/environment.go | 54 +++++++++++++++++++ src/cmd/compile/internal/types2/instantiate.go | 46 ++++++---------- .../compile/internal/types2/instantiate_test.go | 62 ++++++++++++++++++++++ src/cmd/compile/internal/types2/named.go | 22 +++++--- src/cmd/compile/internal/types2/subst.go | 44 +++++++-------- src/cmd/compile/internal/types2/typestring.go | 55 ++++++++----------- 9 files changed, 195 insertions(+), 98 deletions(-) create mode 100644 src/cmd/compile/internal/types2/environment.go create mode 100644 src/cmd/compile/internal/types2/instantiate_test.go diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 296d84289c..6c0d9c8c9d 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -233,7 +233,9 @@ func (r *reader2) doTyp() (res types2.Type) { obj, targs := r.obj() name := obj.(*types2.TypeName) if len(targs) != 0 { - t, _ := types2.Instantiate(types2.NewEnvironment(r.p.check), name.Type(), targs, false) + // TODO(mdempsky) should use a single shared environment here + // (before, this used a shared checker) + t, _ := types2.Instantiate(types2.NewEnvironment(), name.Type(), targs, false) return t } return name.Type() diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 4226b4de82..c7b45d86d1 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -86,7 +86,7 @@ type Checker struct { nextID uint64 // unique Id for type parameters (first valid Id is 1) objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - typMap map[string]*Named // maps an instantiated named type hash to a *Named type + env *Environment // for deduplicating identical instances // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -188,7 +188,7 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - typMap: make(map[string]*Named), + env: NewEnvironment(), } } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index cd97080824..1d46b004b6 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.typMap) + t.expand(check.env) // don't touch the type if it is from a different package or the Universe scope // (doing so would lead to a race condition - was issue #35049) diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go new file mode 100644 index 0000000000..070cf34243 --- /dev/null +++ b/src/cmd/compile/internal/types2/environment.go @@ -0,0 +1,54 @@ +// Copyright 2021 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 types2 + +import "sync" + +// An Environment is an opaque type checking environment. It may be used to +// share identical type instances across type-checked packages or calls to +// Instantiate. +// +// It is safe for concurrent use. +type Environment struct { + mu sync.Mutex + typeMap map[string]*Named // type hash -> instance + nextID int // next unique ID + seen map[*Named]int // assigned unique IDs +} + +// NewEnvironment creates a new Environment. +func NewEnvironment() *Environment { + return &Environment{ + typeMap: make(map[string]*Named), + seen: make(map[*Named]int), + } +} + +// TODO(rfindley): move Environment.typeHash here. +// typeForHash returns the recorded type for the type hash h, if it exists. +// If no type exists for h and n is non-nil, n is recorded for h. +func (env *Environment) typeForHash(h string, n *Named) *Named { + env.mu.Lock() + defer env.mu.Unlock() + if existing := env.typeMap[h]; existing != nil { + return existing + } + if n != nil { + env.typeMap[h] = n + } + return n +} + +// idForType returns a unique ID for the pointer n. +func (env *Environment) idForType(n *Named) int { + env.mu.Lock() + defer env.mu.Unlock() + id, ok := env.seen[n] + if !ok { + id = env.nextID + env.seen[n] = id + env.nextID++ + } + return id +} diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index c882699d1d..d1e981acc4 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -13,21 +13,6 @@ import ( "fmt" ) -// An Environment is an opaque type checking environment. It may be used to -// share identical type instances across type checked packages or calls to -// Instantiate. -type Environment struct { - // For now, Environment just hides a Checker. - // Eventually, we strive to remove the need for a checker. - check *Checker -} - -// NewEnvironment returns a new Environment, initialized with the given -// Checker, or nil. -func NewEnvironment(check *Checker) *Environment { - return &Environment{check} -} - // Instantiate instantiates the type typ with the given type arguments targs. // typ must be a *Named or a *Signature type, and its number of type parameters // must match the number of provided type arguments. The result is a new, @@ -46,11 +31,7 @@ func NewEnvironment(check *Checker) *Environment { // TODO(rfindley): change this function to also return an error if lengths of // tparams and targs do not match. func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, error) { - var check *Checker - if env != nil { - check = env.check - } - inst := check.instance(nopos, typ, targs) + inst := (*Checker)(nil).instance(nopos, typ, targs, env) var err error if validate { @@ -61,7 +42,7 @@ func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, case *Signature: tparams = t.TParams().list() } - if i, err := check.verify(nopos, tparams, targs); err != nil { + if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil { return inst, ArgumentError{i, err} } } @@ -90,7 +71,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis }() } - inst := check.instance(pos, typ, targs) + inst := check.instance(pos, typ, targs, check.env) assert(len(posList) <= len(targs)) check.later(func() { @@ -122,14 +103,15 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis // instance creates a type or function instance using the given original type // typ and arguments targs. For Named types the resulting instance will be // unexpanded. -func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type { +func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Environment) Type { switch t := typ.(type) { case *Named: - h := typeHash(t, targs) - if check != nil { - // typ may already have been instantiated with identical type arguments. - // In that case, re-use the existing instance. - if named := check.typMap[h]; named != nil { + var h string + if env != nil { + h = env.typeHash(t, targs) + // typ may already have been instantiated with identical type arguments. In + // that case, re-use the existing instance. + if named := env.typeForHash(h, nil); named != nil { return named } } @@ -137,8 +119,10 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type { named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded named.targs = NewTypeList(targs) named.instPos = &pos - if check != nil { - check.typMap[h] = named + if env != nil { + // It's possible that we've lost a race to add named to the environment. + // In this case, use whichever instance is recorded in the environment. + named = env.typeForHash(h, named) } return named @@ -150,7 +134,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type) Type { if tparams.Len() == 0 { return typ // nothing to do (minor optimization) } - sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), nil).(*Signature) + sig := check.subst(pos, typ, makeSubstMap(tparams.list(), targs), env).(*Signature) // If the signature doesn't use its type parameters, subst // will not make a copy. In that case, make a copy now (so // we can set tparams to nil w/o causing side-effects). diff --git a/src/cmd/compile/internal/types2/instantiate_test.go b/src/cmd/compile/internal/types2/instantiate_test.go new file mode 100644 index 0000000000..69a26491cb --- /dev/null +++ b/src/cmd/compile/internal/types2/instantiate_test.go @@ -0,0 +1,62 @@ +// Copyright 2021 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 types2_test + +import ( + . "cmd/compile/internal/types2" + "testing" +) + +func TestInstantiateEquality(t *testing.T) { + const src = genericPkg + "p; type T[P any] int" + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + T := pkg.Scope().Lookup("T").Type().(*Named) + // Instantiating the same type twice should result in pointer-equivalent + // instances. + env := NewEnvironment() + res1, err := Instantiate(env, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(env, T, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + if res1 != res2 { + t.Errorf("first instance (%s) not pointer-equivalent to second instance (%s)", res1, res2) + } +} +func TestInstantiateNonEquality(t *testing.T) { + const src = genericPkg + "p; type T[P any] int" + pkg1, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + pkg2, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + // We consider T1 and T2 to be distinct types, so their instances should not + // be deduplicated by the environment. + T1 := pkg1.Scope().Lookup("T").Type().(*Named) + T2 := pkg2.Scope().Lookup("T").Type().(*Named) + env := NewEnvironment() + res1, err := Instantiate(env, T1, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + res2, err := Instantiate(env, T2, []Type{Typ[Int]}, false) + if err != nil { + t.Fatal(err) + } + if res1 == res2 { + t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2) + } + if Identical(res1, res2) { + t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2) + } +} diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index a76e69fcf1..c096c1b30b 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -242,7 +242,7 @@ func (n *Named) setUnderlying(typ Type) { // expand ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func (n *Named) expand(typMap map[string]*Named) *Named { +func (n *Named) expand(env *Environment) *Named { if n.instPos != nil { // n must be loaded before instantiation, in order to have accurate // tparams. This is done implicitly by the call to n.TParams, but making it @@ -250,19 +250,25 @@ func (n *Named) expand(typMap map[string]*Named) *Named { n.load() var u Type if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { - if typMap == nil { + // TODO(rfindley): handling an optional Checker and Environment here (and + // in subst) feels overly complicated. Can we simplify? + if env == nil { if n.check != nil { - typMap = n.check.typMap + env = n.check.env } else { // If we're instantiating lazily, we might be outside the scope of a // type-checking pass. In that case we won't have a pre-existing - // typMap, but don't want to create a duplicate of the current instance - // in the process of expansion. - h := typeHash(n.orig, n.targs.list()) - typMap = map[string]*Named{h: n} + // environment, but don't want to create a duplicate of the current + // instance in the process of expansion. + env = NewEnvironment() } + h := env.typeHash(n.orig, n.targs.list()) + // add the instance to the environment to avoid infinite recursion. + // addInstance may return a different, existing instance, but we + // shouldn't return that instance from expand. + env.typeForHash(h, n) } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), typMap) + u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), env) } else { u = Typ[Invalid] } diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index c67538d4f0..f86555594d 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -40,8 +40,8 @@ func (m substMap) lookup(tpar *TypeParam) Type { // incoming type. If a substitution took place, the result type is different // from the incoming type. // -// If the given typMap is non-nil, it is used in lieu of check.typMap. -func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[string]*Named) Type { +// If the given environment is non-nil, it is used in lieu of check.env. +func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Environment) Type { if smap.empty() { return typ } @@ -61,27 +61,27 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, typMap map[ if check != nil { subst.check = check - if typMap == nil { - typMap = check.typMap + if env == nil { + env = check.env } } - if typMap == nil { + if env == nil { // If we don't have a *Checker and its global type map, // use a local version. Besides avoiding duplicate work, // the type map prevents infinite recursive substitution // for recursive types (example: type T[P any] *T[P]). - typMap = make(map[string]*Named) + env = NewEnvironment() } - subst.typMap = typMap + subst.env = env return subst.typ(typ) } type subster struct { - pos syntax.Pos - smap substMap - check *Checker // nil if called via Instantiate - typMap map[string]*Named + pos syntax.Pos + smap substMap + check *Checker // nil if called via Instantiate + env *Environment } func (subst *subster) typ(typ Type) Type { @@ -214,25 +214,25 @@ func (subst *subster) typ(typ Type) Type { } // before creating a new named type, check if we have this one already - h := typeHash(t, newTArgs) + h := subst.env.typeHash(t.orig, newTArgs) dump(">>> new type hash: %s", h) - if named, found := subst.typMap[h]; found { + if named := subst.env.typeForHash(h, nil); named != nil { dump(">>> found %s", named) return named } - // Create a new named type and populate typMap to avoid endless recursion. - // The position used here is irrelevant because validation only occurs on t - // (we don't call validType on named), but we use subst.pos to help with - // debugging. + // Create a new named type and populate the environment to avoid endless + // recursion. The position used here is irrelevant because validation only + // occurs on t (we don't call validType on named), but we use subst.pos to + // help with debugging. tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) t.load() // It's ok to provide a nil *Checker because the newly created type // doesn't need to be (lazily) expanded; it's expanded below. named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available named.targs = NewTypeList(newTArgs) - subst.typMap[h] = named - t.expand(subst.typMap) // must happen after typMap update to avoid infinite recursion + subst.env.typeForHash(h, named) + t.expand(subst.env) // must happen after env update to avoid infinite recursion // do the substitution dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs) @@ -257,14 +257,16 @@ func (subst *subster) typ(typ Type) Type { // type hash: types that are identical produce identical string representations. // If typ is a *Named type and targs is not empty, typ is printed as if it were // instantiated with targs. -func typeHash(typ Type, targs []Type) string { +func (env *Environment) typeHash(typ Type, targs []Type) string { + assert(env != nil) assert(typ != nil) var buf bytes.Buffer - h := newTypeHasher(&buf) + h := newTypeHasher(&buf, env) if named, _ := typ.(*Named); named != nil && len(targs) > 0 { // Don't use WriteType because we need to use the provided targs // and not any targs that might already be with the *Named type. + h.typePrefix(named) h.typeName(named.obj) h.typeList(targs) } else { diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 6083955306..23fd788fbe 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -9,6 +9,7 @@ package types2 import ( "bytes" "fmt" + "strconv" "unicode/utf8" ) @@ -70,22 +71,23 @@ type typeWriter struct { buf *bytes.Buffer seen map[Type]bool qf Qualifier - hash bool + env *Environment // if non-nil, we are type hashing } func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { - return &typeWriter{buf, make(map[Type]bool), qf, false} + return &typeWriter{buf, make(map[Type]bool), qf, nil} } -func newTypeHasher(buf *bytes.Buffer) *typeWriter { - return &typeWriter{buf, make(map[Type]bool), nil, true} +func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter { + assert(env != nil) + return &typeWriter{buf, make(map[Type]bool), nil, env} } func (w *typeWriter) byte(b byte) { w.buf.WriteByte(b) } func (w *typeWriter) string(s string) { w.buf.WriteString(s) } func (w *typeWriter) writef(format string, args ...interface{}) { fmt.Fprintf(w.buf, format, args...) } func (w *typeWriter) error(msg string) { - if w.hash { + if w.env != nil { panic(msg) } w.string("<" + msg + ">") @@ -227,14 +229,15 @@ func (w *typeWriter) typ(typ Type) { // types. Write them to aid debugging, but don't write // them when we need an instance hash: whether a type // is fully expanded or not doesn't matter for identity. - if !w.hash && t.instPos != nil { + if w.env == nil && t.instPos != nil { w.byte(instanceMarker) } + w.typePrefix(t) w.typeName(t.obj) if t.targs != nil { // instantiated type w.typeList(t.targs.list()) - } else if !w.hash && t.TParams().Len() != 0 { // For type hashing, don't need to format the TParams + } else if w.env == nil && t.TParams().Len() != 0 { // For type hashing, don't need to format the TParams // parameterized type w.tParamList(t.TParams().list()) } @@ -263,6 +266,15 @@ func (w *typeWriter) typ(typ Type) { } } +// If w.env is non-nil, typePrefix writes a unique prefix for the named type t +// based on the types already observed by w.env. If w.env is nil, it does +// nothing. +func (w *typeWriter) typePrefix(t *Named) { + if w.env != nil { + w.string(strconv.Itoa(w.env.idForType(t))) + } +} + func (w *typeWriter) typeList(list []Type) { w.byte('[') for i, typ := range list { @@ -308,31 +320,6 @@ func (w *typeWriter) typeName(obj *TypeName) { writePackage(w.buf, obj.pkg, w.qf) } w.string(obj.name) - - if w.hash { - // For local defined types, use the (original!) TypeName's scope - // numbers to disambiguate. - if typ, _ := obj.typ.(*Named); typ != nil { - // TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes - // and whether the loop can iterate more than twice. - // (It seems somehow connected to instance types.) - for typ.orig != typ { - typ = typ.orig - } - w.writeScopeNumbers(typ.obj.parent) - } - } -} - -// writeScopeNumbers writes the number sequence for this scope to buf -// in the form ".i.j.k" where i, j, k, etc. stand for scope numbers. -// If a scope is nil or has no parent (such as a package scope), nothing -// is written. -func (w *typeWriter) writeScopeNumbers(s *Scope) { - if s != nil && s.number > 0 { - w.writeScopeNumbers(s.parent) - w.writef(".%d", s.number) - } } func (w *typeWriter) tuple(tup *Tuple, variadic bool) { @@ -343,7 +330,7 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { w.string(", ") } // parameter names are ignored for type identity and thus type hashes - if !w.hash && v.name != "" { + if w.env == nil && v.name != "" { w.string(v.name) w.byte(' ') } @@ -384,7 +371,7 @@ func (w *typeWriter) signature(sig *Signature) { } w.byte(' ') - if n == 1 && (w.hash || sig.results.vars[0].name == "") { + if n == 1 && (w.env != nil || sig.results.vars[0].name == "") { // single unnamed result (if type hashing, name must be ignored) w.typ(sig.results.vars[0].typ) return -- cgit v1.2.3-54-g00ecf From ccc927b8f6550cb638e78fd63eebf422fc3c3d8a Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 13:37:34 -0700 Subject: cmd/compile/internal/types2: move typeHash to environment.go This is a clean port of CL 347560. Change-Id: I0d56f5a818df1a66e603415d5198d909b0aef228 Reviewed-on: https://go-review.googlesource.com/c/go/+/348573 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/environment.go | 37 ++++++++++++++++++++++++-- src/cmd/compile/internal/types2/subst.go | 36 +------------------------ 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go index 070cf34243..816139bbb4 100644 --- a/src/cmd/compile/internal/types2/environment.go +++ b/src/cmd/compile/internal/types2/environment.go @@ -3,7 +3,10 @@ // license that can be found in the LICENSE file. package types2 -import "sync" +import ( + "bytes" + "sync" +) // An Environment is an opaque type checking environment. It may be used to // share identical type instances across type-checked packages or calls to @@ -25,7 +28,37 @@ func NewEnvironment() *Environment { } } -// TODO(rfindley): move Environment.typeHash here. +// typeHash returns a string representation of typ, which can be used as an exact +// type hash: types that are identical produce identical string representations. +// If typ is a *Named type and targs is not empty, typ is printed as if it were +// instantiated with targs. +func (env *Environment) typeHash(typ Type, targs []Type) string { + assert(env != nil) + assert(typ != nil) + var buf bytes.Buffer + + h := newTypeHasher(&buf, env) + if named, _ := typ.(*Named); named != nil && len(targs) > 0 { + // Don't use WriteType because we need to use the provided targs + // and not any targs that might already be with the *Named type. + h.typePrefix(named) + h.typeName(named.obj) + h.typeList(targs) + } else { + assert(targs == nil) + h.typ(typ) + } + + if debug { + // there should be no instance markers in type hashes + for _, b := range buf.Bytes() { + assert(b != instanceMarker) + } + } + + return buf.String() +} + // typeForHash returns the recorded type for the type hash h, if it exists. // If no type exists for h and n is non-nil, n is recorded for h. func (env *Environment) typeForHash(h string, n *Named) *Named { diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index f86555594d..2032305fab 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -6,10 +6,7 @@ package types2 -import ( - "bytes" - "cmd/compile/internal/syntax" -) +import "cmd/compile/internal/syntax" type substMap map[*TypeParam]Type @@ -253,37 +250,6 @@ func (subst *subster) typ(typ Type) Type { return typ } -// typeHash returns a string representation of typ, which can be used as an exact -// type hash: types that are identical produce identical string representations. -// If typ is a *Named type and targs is not empty, typ is printed as if it were -// instantiated with targs. -func (env *Environment) typeHash(typ Type, targs []Type) string { - assert(env != nil) - assert(typ != nil) - var buf bytes.Buffer - - h := newTypeHasher(&buf, env) - if named, _ := typ.(*Named); named != nil && len(targs) > 0 { - // Don't use WriteType because we need to use the provided targs - // and not any targs that might already be with the *Named type. - h.typePrefix(named) - h.typeName(named.obj) - h.typeList(targs) - } else { - assert(targs == nil) - h.typ(typ) - } - - if debug { - // there should be no instance markers in type hashes - for _, b := range buf.Bytes() { - assert(b != instanceMarker) - } - } - - return buf.String() -} - // typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid]. // A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_)) // where an array/slice element is accessed before it is set up. -- cgit v1.2.3-54-g00ecf From 47f3e1e02c8737fd06397d957506f3724737cae0 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 13:45:16 -0700 Subject: cmd/compile/internal/types2: move NewTypeParam off of Checker This is a port of CL 347561. A comment was corrected both in types2 and go/types, and the compiler adjusted for the updated NewTypeParameter function. Change-Id: I4381f0dd8e43228e1d037c5d997d421b7838f905 Reviewed-on: https://go-review.googlesource.com/c/go/+/348574 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/importer/iimport.go | 2 +- src/cmd/compile/internal/noder/reader2.go | 2 +- src/cmd/compile/internal/types2/builtins.go | 2 +- src/cmd/compile/internal/types2/decl.go | 2 +- src/cmd/compile/internal/types2/typeparam.go | 10 +++++++--- src/go/types/typeparam.go | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 38cb8db235..646cad60d9 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -365,7 +365,7 @@ func (r *importReader) obj(name string) { } name0, sub := parseSubscript(name) tn := types2.NewTypeName(pos, r.currPkg, name0, nil) - t := (*types2.Checker)(nil).NewTypeParam(tn, nil) + t := types2.NewTypeParam(tn, nil) if sub == 0 { errorf("missing subscript") } diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 6c0d9c8c9d..a5e925b3db 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -483,7 +483,7 @@ func (r *reader2) typeParamNames() []*types2.TypeParam { pkg, name := r.localIdent() tname := types2.NewTypeName(pos, pkg, name, nil) - r.dict.tparams[i] = r.p.check.NewTypeParam(tname, nil) + r.dict.tparams[i] = types2.NewTypeParam(tname, nil) } for i, bound := range r.dict.bounds { diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index e3844d5163..3b8d85859a 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -826,7 +826,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type { // type param is placed in the current package so export/import // works as expected. tpar := NewTypeName(nopos, check.pkg, "", nil) - ptyp := check.NewTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect + ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect ptyp.index = tp.index return ptyp diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 1d46b004b6..5be4a9f804 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -648,7 +648,7 @@ func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam { // constraints to make sure we don't rely on them if they // are not properly set yet. tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil) - tpar := check.NewTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position return tpar } diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index 445337fee8..e7181281af 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -32,15 +32,19 @@ func (t *TypeParam) Obj() *TypeName { return t.obj } // or Signature type by calling SetTParams. Setting a type parameter on more // than one type will result in a panic. // -// The bound argument can be nil, and set later via SetBound. -func (check *Checker) NewTypeParam(obj *TypeName, bound Type) *TypeParam { +// The constraint argument can be nil, and set later via SetConstraint. +func NewTypeParam(obj *TypeName, constraint Type) *TypeParam { + return (*Checker)(nil).newTypeParam(obj, constraint) +} + +func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam { // Always increment lastID, even if it is not used. id := nextID() if check != nil { check.nextID++ id = check.nextID } - typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: bound} + typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint} if obj.typ == nil { obj.typ = typ } diff --git a/src/go/types/typeparam.go b/src/go/types/typeparam.go index a0f2a3acd0..150ad079a8 100644 --- a/src/go/types/typeparam.go +++ b/src/go/types/typeparam.go @@ -32,7 +32,7 @@ type TypeParam struct { // or Signature type by calling SetTypeParams. Setting a type parameter on more // than one type will result in a panic. // -// The bound argument can be nil, and set later via SetConstraint. +// The constraint argument can be nil, and set later via SetConstraint. func NewTypeParam(obj *TypeName, constraint Type) *TypeParam { return (*Checker)(nil).newTypeParam(obj, constraint) } -- cgit v1.2.3-54-g00ecf From b61e1ed863886743598c6f816b0740f3768b50f6 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 13:52:46 -0700 Subject: cmd/compile/internal/types2: temporarily pin the Checker to Interface during checking This is a clean port of CL 348371. Change-Id: I3a61a5a8928279bc783ef16739f7607de3b6ecf3 Reviewed-on: https://go-review.googlesource.com/c/go/+/348575 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/interface.go | 15 ++++++++++++--- src/cmd/compile/internal/types2/sizeof_test.go | 2 +- .../internal/types2/testdata/fixedbugs/issue48234.go2 | 10 ++++++++++ src/cmd/compile/internal/types2/universe.go | 4 ++-- 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index e57158d2d5..340df51524 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -11,6 +11,7 @@ import "cmd/compile/internal/syntax" // An Interface represents an interface type. type Interface struct { + check *Checker // for error reporting; nil once type set is computed obj *TypeName // corresponding declared object; or nil (for better error messages) methods []*Func // ordered list of explicitly declared methods embeddeds []Type // ordered list of explicitly embedded elements @@ -21,7 +22,7 @@ type Interface struct { } // typeSet returns the type set for interface t. -func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(nil, nopos, t) } +func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) } // emptyInterface represents the empty interface var emptyInterface = Interface{complete: true, tset: &topTypeSet} @@ -198,7 +199,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType } // All methods and embedded elements for this interface are collected; - // i.e., this interface is may be used in a type set computation. + // i.e., this interface may be used in a type set computation. ityp.complete = true if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 { @@ -214,7 +215,15 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType // Compute type set with a non-nil *Checker as soon as possible // to report any errors. Subsequent uses of type sets will use // this computed type set and won't need to pass in a *Checker. - check.later(func() { computeInterfaceTypeSet(check, iface.Pos(), ityp) }) + // + // Pin the checker to the interface type in the interim, in case the type set + // must be used before delayed funcs are processed (see issue #48234). + // TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet + ityp.check = check + check.later(func() { + computeInterfaceTypeSet(check, iface.Pos(), ityp) + ityp.check = nil + }) } func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr { diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index 5be369d843..bbaca8e0aa 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -28,7 +28,7 @@ func TestSizeof(t *testing.T) { {Tuple{}, 12, 24}, {Signature{}, 28, 56}, {Union{}, 16, 32}, - {Interface{}, 40, 80}, + {Interface{}, 44, 88}, {Map{}, 16, 32}, {Chan{}, 12, 24}, {Named{}, 72, 136}, diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 new file mode 100644 index 0000000000..e069930c42 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 @@ -0,0 +1,10 @@ +// Copyright 2021 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 p + +var _ = interface{ + m() + m /* ERROR "duplicate method" */ () +}(nil) diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index a615b4c876..af3ab97325 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -88,7 +88,7 @@ func defPredeclaredTypes() { res := NewVar(nopos, nil, "", Typ[String]) sig := NewSignature(nil, nil, NewTuple(res), false) err := NewFunc(nopos, nil, "Error", sig) - ityp := &Interface{obj, []*Func{err}, nil, nil, true, nil} + ityp := &Interface{nil, obj, []*Func{err}, nil, nil, true, nil} computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset typ := NewNamed(obj, ityp, nil) sig.recv = NewVar(nopos, nil, "", typ) @@ -99,7 +99,7 @@ func defPredeclaredTypes() { { obj := NewTypeName(nopos, nil, "comparable", nil) obj.setColor(black) - ityp := &Interface{obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}} + ityp := &Interface{nil, obj, nil, nil, nil, true, &_TypeSet{true, nil, allTermlist}} NewNamed(obj, ityp, nil) def(obj) } -- cgit v1.2.3-54-g00ecf From bff39cf6cb5bbe1575fdd95682c991ef35e97289 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Sat, 4 Sep 2021 19:29:08 -0700 Subject: cmd/compile: add automated rewrite cycle detection A common bug during development is to introduce rewrite rule cycles. This is annoying because it takes a while to notice that make.bash is a bit too slow this time, and to remember why. And then you have to manually arrange to debug. Make this all easier by automating it. Detect cycles, and when we detect one, print the sequence of rewrite rules that occur within a single cycle before crashing. Change-Id: I8dadda13990ab925a81940d4833c9e5243368435 Reviewed-on: https://go-review.googlesource.com/c/go/+/347829 Trust: Josh Bleecher Snyder Run-TryBot: Josh Bleecher Snyder TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/ssa/html.go | 2 +- src/cmd/compile/internal/ssa/print.go | 32 +++++++++++++++++++++++++------- src/cmd/compile/internal/ssa/rewrite.go | 26 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 4d191199fb..6fd898636c 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -1221,7 +1221,7 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { } } -func (p htmlFuncPrinter) endBlock(b *Block) { +func (p htmlFuncPrinter) endBlock(b *Block, reachable bool) { if len(b.Values) > 0 { // end list of values io.WriteString(p.w, "") io.WriteString(p.w, "") diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go index d917183c70..81c64a7692 100644 --- a/src/cmd/compile/internal/ssa/print.go +++ b/src/cmd/compile/internal/ssa/print.go @@ -17,22 +17,30 @@ func printFunc(f *Func) { func hashFunc(f *Func) []byte { h := sha256.New() - p := stringFuncPrinter{w: h} + p := stringFuncPrinter{w: h, printDead: true} fprintFunc(p, f) return h.Sum(nil) } func (f *Func) String() string { var buf bytes.Buffer - p := stringFuncPrinter{w: &buf} + p := stringFuncPrinter{w: &buf, printDead: true} fprintFunc(p, f) return buf.String() } +// rewriteHash returns a hash of f suitable for detecting rewrite cycles. +func (f *Func) rewriteHash() string { + h := sha256.New() + p := stringFuncPrinter{w: h, printDead: false} + fprintFunc(p, f) + return fmt.Sprintf("%x", h.Sum(nil)) +} + type funcPrinter interface { header(f *Func) startBlock(b *Block, reachable bool) - endBlock(b *Block) + endBlock(b *Block, reachable bool) value(v *Value, live bool) startDepCycle() endDepCycle() @@ -40,7 +48,8 @@ type funcPrinter interface { } type stringFuncPrinter struct { - w io.Writer + w io.Writer + printDead bool } func (p stringFuncPrinter) header(f *Func) { @@ -50,6 +59,9 @@ func (p stringFuncPrinter) header(f *Func) { } func (p stringFuncPrinter) startBlock(b *Block, reachable bool) { + if !p.printDead && !reachable { + return + } fmt.Fprintf(p.w, " b%d:", b.ID) if len(b.Preds) > 0 { io.WriteString(p.w, " <-") @@ -64,11 +76,17 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) { io.WriteString(p.w, "\n") } -func (p stringFuncPrinter) endBlock(b *Block) { +func (p stringFuncPrinter) endBlock(b *Block, reachable bool) { + if !p.printDead && !reachable { + return + } fmt.Fprintln(p.w, " "+b.LongString()) } func (p stringFuncPrinter) value(v *Value, live bool) { + if !p.printDead && !live { + return + } fmt.Fprint(p.w, " ") //fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos)) //fmt.Fprint(p.w, ": ") @@ -103,7 +121,7 @@ func fprintFunc(p funcPrinter, f *Func) { p.value(v, live[v.ID]) printed[v.ID] = true } - p.endBlock(b) + p.endBlock(b, reachable[b.ID]) continue } @@ -151,7 +169,7 @@ func fprintFunc(p funcPrinter, f *Func) { } } - p.endBlock(b) + p.endBlock(b, reachable[b.ID]) } for _, name := range f.Names { p.named(*name, f.NamedValues[*name]) diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 5d468768b6..a997050ee2 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -36,6 +36,8 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu if debug > 1 { fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name) } + var iters int + var states map[string]bool for { change := false for _, b := range f.Blocks { @@ -146,6 +148,30 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu if !change { break } + iters++ + if iters > 1000 || debug >= 2 { + // We've done a suspiciously large number of rewrites (or we're in debug mode). + // As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer + // and the maximum value encountered during make.bash is 12. + // Start checking for cycles. (This is too expensive to do routinely.) + if states == nil { + states = make(map[string]bool) + } + h := f.rewriteHash() + if _, ok := states[h]; ok { + // We've found a cycle. + // To diagnose it, set debug to 2 and start again, + // so that we'll print all rules applied until we complete another cycle. + // If debug is already >= 2, we've already done that, so it's time to crash. + if debug < 2 { + debug = 2 + states = make(map[string]bool) + } else { + f.Fatalf("rewrite cycle detected") + } + } + states[h] = true + } } // remove clobbered values for _, b := range f.Blocks { -- cgit v1.2.3-54-g00ecf From f5f8a911d8425995c61ed836584b4f3ad0e4c8fc Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 15:22:20 -0700 Subject: cmd/compile/internal/types2: spell out 'Type' in type parameter APIs This is a port of CL 348376 with the necessary adjustments in the compiler. Change-Id: Ib11ee841b194746ff231ee493aa56bf9b3a4a67f Reviewed-on: https://go-review.googlesource.com/c/go/+/348577 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/importer/iimport.go | 6 ++-- src/cmd/compile/internal/noder/decl.go | 2 +- src/cmd/compile/internal/noder/expr.go | 4 +-- src/cmd/compile/internal/noder/reader2.go | 2 +- src/cmd/compile/internal/noder/types.go | 12 ++++---- src/cmd/compile/internal/noder/writer.go | 28 ++++++++--------- src/cmd/compile/internal/types2/api_test.go | 2 +- src/cmd/compile/internal/types2/assignments.go | 2 +- src/cmd/compile/internal/types2/call.go | 18 +++++------ src/cmd/compile/internal/types2/decl.go | 4 +-- src/cmd/compile/internal/types2/index.go | 2 +- src/cmd/compile/internal/types2/infer.go | 2 +- src/cmd/compile/internal/types2/instantiate.go | 10 +++--- src/cmd/compile/internal/types2/lookup.go | 16 +++++----- src/cmd/compile/internal/types2/named.go | 42 +++++++++++++------------- src/cmd/compile/internal/types2/object.go | 4 +-- src/cmd/compile/internal/types2/predicates.go | 8 ++--- src/cmd/compile/internal/types2/signature.go | 28 ++++++++--------- src/cmd/compile/internal/types2/subst.go | 6 ++-- src/cmd/compile/internal/types2/typelists.go | 16 +++++----- src/cmd/compile/internal/types2/typeparam.go | 2 +- src/cmd/compile/internal/types2/typestring.go | 8 ++--- 22 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 646cad60d9..8fdd879705 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -314,7 +314,7 @@ func (r *importReader) obj(name string) { tparams = r.tparamList() } sig := r.signature(nil) - sig.SetTParams(tparams) + sig.SetTypeParams(tparams) r.declare(types2.NewFunc(pos, r.currPkg, name, sig)) case 'T', 'U': @@ -327,7 +327,7 @@ func (r *importReader) obj(name string) { // declaration before recursing. obj := types2.NewTypeName(pos, r.currPkg, name, nil) named := types2.NewNamed(obj, nil, nil) - named.SetTParams(tparams) + named.SetTypeParams(tparams) r.declare(obj) underlying := r.p.typAt(r.uint64(), named).Underlying() @@ -343,7 +343,7 @@ func (r *importReader) obj(name string) { // If the receiver has any targs, set those as the // rparams of the method (since those are the // typeparams being used in the method sig/body). - targs := baseType(msig.Recv().Type()).TArgs() + targs := baseType(msig.Recv().Type()).TypeArgs() if targs.Len() > 0 { rparams := make([]*types2.TypeParam, targs.Len()) for i := range rparams { diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go index de481fb5fc..c9ab31f203 100644 --- a/src/cmd/compile/internal/noder/decl.go +++ b/src/cmd/compile/internal/noder/decl.go @@ -190,7 +190,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) { // object to new type pragmas.] ntyp.SetUnderlying(g.typeExpr(decl.Type)) - tparams := otyp.(*types2.Named).TParams() + tparams := otyp.(*types2.Named).TypeParams() if n := tparams.Len(); n > 0 { rparams := make([]*types.Type, n) for i := range rparams { diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index 7dbbc88f8f..5eeafddae2 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -344,7 +344,7 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto if wantPtr { recvType2Base = types2.AsPointer(recvType2).Elem() } - if types2.AsNamed(recvType2Base).TParams().Len() > 0 { + if types2.AsNamed(recvType2Base).TypeParams().Len() > 0 { // recvType2 is the original generic type that is // instantiated for this method call. // selinfo.Recv() is the instantiated type @@ -395,7 +395,7 @@ func getTargs(selinfo *types2.Selection) *types2.TypeList { if n == nil { base.Fatalf("Incorrect type for selinfo %v", selinfo) } - return n.TArgs() + return n.TypeArgs() } func (g *irgen) exprList(expr syntax.Expr) []ir.Node { diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index a5e925b3db..3886d571b5 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -397,7 +397,7 @@ func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) { pos := r.pos() tparams := r.typeParamNames() sig := r.signature(nil) - sig.SetTParams(tparams) + sig.SetTypeParams(tparams) return types2.NewFunc(pos, objPkg, objName, sig) case objType: diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index 5c9aafe490..b0b9c1592a 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -91,7 +91,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { // since that is the only use of a generic type that doesn't // involve instantiation. We just translate the named type in the // normal way below using g.obj(). - if typ.TParams() != nil && typ.TArgs() != nil { + if typ.TypeParams() != nil && typ.TypeArgs() != nil { // typ is an instantiation of a defined (named) generic type. // This instantiation should also be a defined (named) type. // types2 gives us the substituted type in t.Underlying() @@ -101,7 +101,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { // // When converted to types.Type, typ has a unique name, // based on the names of the type arguments. - instName := g.instTypeName2(typ.Obj().Name(), typ.TArgs()) + instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs()) s := g.pkg(typ.Obj().Pkg()).Lookup(instName) if s.Def != nil { // We have already encountered this instantiation. @@ -135,7 +135,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { // non-generic types used to instantiate this type. We'll // use these when instantiating the methods of the // instantiated type. - targs := typ.TArgs() + targs := typ.TypeArgs() rparams := make([]*types.Type, targs.Len()) for i := range rparams { rparams[i] = g.typ1(targs.At(i)) @@ -272,7 +272,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type { // instantiated types, and for actually generating the methods for instantiated // types. func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { - targs2 := typ.TArgs() + targs2 := typ.TypeArgs() targs := make([]*types.Type, targs2.Len()) for i := range targs { targs[i] = g.typ1(targs2.At(i)) @@ -296,7 +296,7 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { // generic type, so we have to do a substitution to get // the name/type of the method of the instantiated type, // using m.Type().RParams() and typ.TArgs() - inst2 := g.instTypeName2("", typ.TArgs()) + inst2 := g.instTypeName2("", typ.TypeArgs()) name := meth.Sym().Name i1 := strings.Index(name, "[") i2 := strings.Index(name[i1:], "]") @@ -336,7 +336,7 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { } func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type { - tparams2 := sig.TParams() + tparams2 := sig.TypeParams() tparams := make([]*types.Field, tparams2.Len()) for i := range tparams { tp := tparams2.At(i).Obj() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 1405c77161..d1e5605739 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -299,16 +299,16 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo { // Type aliases can refer to uninstantiated generic types, so we // might see len(TParams) != 0 && len(TArgs) == 0 here. // TODO(mdempsky): Revisit after #46477 is resolved. - assert(typ.TParams().Len() == typ.TArgs().Len() || typ.TArgs().Len() == 0) + assert(typ.TypeParams().Len() == typ.TypeArgs().Len() || typ.TypeArgs().Len() == 0) // TODO(mdempsky): Why do we need to loop here? orig := typ - for orig.TArgs() != nil { + for orig.TypeArgs() != nil { orig = orig.Orig() } w.code(typeNamed) - w.obj(orig.Obj(), typ.TArgs()) + w.obj(orig.Obj(), typ.TypeArgs()) case *types2.TypeParam: index := func() int { @@ -345,7 +345,7 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo { w.typ(typ.Elem()) case *types2.Signature: - assert(typ.TParams() == nil) + assert(typ.TypeParams() == nil) w.code(typeSignature) w.signature(typ) @@ -405,7 +405,7 @@ func (w *writer) interfaceType(typ *types2.Interface) { for i := 0; i < typ.NumExplicitMethods(); i++ { m := typ.ExplicitMethod(i) sig := m.Type().(*types2.Signature) - assert(sig.TParams() == nil) + assert(sig.TypeParams() == nil) w.pos(m) w.selector(m) @@ -551,7 +551,7 @@ func (w *writer) doObj(obj types2.Object) codeObj { sig := obj.Type().(*types2.Signature) w.pos(obj) - w.typeParamNames(sig.TParams()) + w.typeParamNames(sig.TypeParams()) w.signature(sig) w.pos(decl) w.ext.funcExt(obj) @@ -568,10 +568,10 @@ func (w *writer) doObj(obj types2.Object) codeObj { } named := obj.Type().(*types2.Named) - assert(named.TArgs() == nil) + assert(named.TypeArgs() == nil) w.pos(obj) - w.typeParamNames(named.TParams()) + w.typeParamNames(named.TypeParams()) w.ext.typeExt(obj) w.typExpr(decl.Type) @@ -642,7 +642,7 @@ func (w *writer) objDict(obj types2.Object, dict *writerDict) { assert(len(dict.funcs) == nfuncs) } -func (w *writer) typeParamNames(tparams *types2.TParamList) { +func (w *writer) typeParamNames(tparams *types2.TypeParamList) { w.sync(syncTypeParamNames) ntparams := tparams.Len() @@ -1677,7 +1677,7 @@ func (w *writer) pkgDecl(decl syntax.Decl) { obj := w.p.info.Defs[decl.Name].(*types2.Func) sig := obj.Type().(*types2.Signature) - if sig.RParams() != nil || sig.TParams() != nil { + if sig.RParams() != nil || sig.TypeParams() != nil { break // skip generic functions } @@ -1711,7 +1711,7 @@ func (w *writer) pkgDecl(decl syntax.Decl) { // TODO(mdempsky): Revisit after #46477 is resolved. if name.IsAlias() { named, ok := name.Type().(*types2.Named) - if ok && named.TParams().Len() != 0 && named.TArgs().Len() == 0 { + if ok && named.TypeParams().Len() != 0 && named.TypeArgs().Len() == 0 { break } } @@ -1858,17 +1858,17 @@ func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int { } // objTypeParams returns the type parameters on the given object. -func objTypeParams(obj types2.Object) *types2.TParamList { +func objTypeParams(obj types2.Object) *types2.TypeParamList { switch obj := obj.(type) { case *types2.Func: sig := obj.Type().(*types2.Signature) if sig.Recv() != nil { return sig.RParams() } - return sig.TParams() + return sig.TypeParams() case *types2.TypeName: if !obj.IsAlias() { - return obj.Type().(*types2.Named).TParams() + return obj.Type().(*types2.Named).TypeParams() } } return nil diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 039a6c0e5e..3ec0d78a23 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -1871,7 +1871,7 @@ func TestInstantiate(t *testing.T) { // type T should have one type parameter T := pkg.Scope().Lookup("T").Type().(*Named) - if n := T.TParams().Len(); n != 1 { + if n := T.TypeParams().Len(); n != 1 { t.Fatalf("expected 1 type parameter; found %d", n) } diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go index 6184fc2ea5..29d63cf819 100644 --- a/src/cmd/compile/internal/types2/assignments.go +++ b/src/cmd/compile/internal/types2/assignments.go @@ -68,7 +68,7 @@ func (check *Checker) assignment(x *operand, T Type, context string) { // x.typ is typed // A generic (non-instantiated) function value cannot be assigned to a variable. - if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 { + if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 { check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context) } diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 5bf17876c1..f6aaa461b9 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -30,7 +30,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // check number of type arguments (got) vs number of type parameters (want) sig := x.typ.(*Signature) - got, want := len(targs), sig.TParams().Len() + got, want := len(targs), sig.TypeParams().Len() if !useConstraintTypeInference && got != want || got > want { check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want) x.mode = invalid @@ -41,7 +41,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // if we don't have enough type arguments, try type inference inferred := false if got < want { - targs = check.infer(inst.Pos(), sig.TParams().list(), targs, nil, nil, true) + targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil, true) if targs == nil { // error was already reported x.mode = invalid @@ -61,7 +61,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) { // instantiate function signature res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) - assert(res.TParams().Len() == 0) // signature is not generic anymore + assert(res.TypeParams().Len() == 0) // signature is not generic anymore if inferred { check.recordInferred(inst, targs, res) } @@ -166,7 +166,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { assert(len(targs) == len(xlist)) // check number of type arguments (got) vs number of type parameters (want) - got, want := len(targs), sig.TParams().Len() + got, want := len(targs), sig.TypeParams().Len() if got > want { check.errorf(xlist[want], "got %d type arguments but want %d", got, want) check.use(call.ArgList...) @@ -200,7 +200,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind { // if type inference failed, a parametrized result must be invalidated // (operands cannot have a parametrized type) - if x.mode == value && sig.TParams().Len() > 0 && isParameterized(sig.TParams().list(), x.typ) { + if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) { x.mode = invalid } @@ -328,7 +328,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // infer type arguments and instantiate signature if necessary - if sig.TParams().Len() > 0 { + if sig.TypeParams().Len() > 0 { if !check.allowVersion(check.pkg, 1, 18) { if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil { check.softErrorf(iexpr.Pos(), "function instantiation requires go1.18 or later") @@ -338,21 +338,21 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T } // TODO(gri) provide position information for targs so we can feed // it to the instantiate call for better error reporting - targs := check.infer(call.Pos(), sig.TParams().list(), targs, sigParams, args, true) + targs := check.infer(call.Pos(), sig.TypeParams().list(), targs, sigParams, args, true) if targs == nil { return // error already reported } // compute result signature rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) - assert(rsig.TParams().Len() == 0) // signature is not generic anymore + assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore check.recordInferred(call, targs, rsig) // Optimization: Only if the parameter list was adjusted do we // need to compute it from the adjusted list; otherwise we can // simply use the result signature's parameter list. if adjusted { - sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TParams().list(), targs), nil).(*Tuple) + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple) } else { sigParams = rsig.params } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 5be4a9f804..4181be9fa8 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -592,13 +592,13 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named named.underlying = under(named) // If the RHS is a type parameter, it must be from this type declaration. - if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TParams().list(), tpar) < 0 { + if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TypeParams().list(), tpar) < 0 { check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar) named.underlying = Typ[Invalid] } } -func (check *Checker) collectTypeParams(dst **TParamList, list []*syntax.Field) { +func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Field) { tparams := make([]*TypeParam, len(list)) // Declare type parameters up-front. diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go index febfd21ea3..848a70dea8 100644 --- a/src/cmd/compile/internal/types2/index.go +++ b/src/cmd/compile/internal/types2/index.go @@ -34,7 +34,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo return false case value: - if sig := asSignature(x.typ); sig != nil && sig.TParams().Len() > 0 { + if sig := asSignature(x.typ); sig != nil && sig.TypeParams().Len() > 0 { // function instantiation return true } diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index bb7270b346..c2a8155dc7 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -562,7 +562,7 @@ func (w *cycleFinder) typ(typ Type) { w.typ(t.elem) case *Named: - for _, tpar := range t.TArgs().list() { + for _, tpar := range t.TypeArgs().list() { w.typ(tpar) } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index d1e981acc4..3ea21f921b 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -38,9 +38,9 @@ func Instantiate(env *Environment, typ Type, targs []Type, validate bool) (Type, var tparams []*TypeParam switch t := typ.(type) { case *Named: - tparams = t.TParams().list() + tparams = t.TypeParams().list() case *Signature: - tparams = t.TParams().list() + tparams = t.TypeParams().list() } if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil { return inst, ArgumentError{i, err} @@ -80,9 +80,9 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis var tparams []*TypeParam switch t := typ.(type) { case *Named: - tparams = t.TParams().list() + tparams = t.TypeParams().list() case *Signature: - tparams = t.TParams().list() + tparams = t.TypeParams().list() } // Avoid duplicate errors; instantiate will have complained if tparams // and targs do not have the same length. @@ -127,7 +127,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Envi return named case *Signature: - tparams := t.TParams() + tparams := t.TypeParams() if !check.validateTArgLen(pos, tparams.Len(), len(targs)) { return Typ[Invalid] } diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index d0718e51e2..67cdc1e68a 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -321,10 +321,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if ftyp.TParams().Len() != mtyp.TParams().Len() { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } - if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 { panic("method with type parameters") } @@ -334,7 +334,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) u := newUnifier(true) - u.x.init(ftyp.TParams().list()) + u.x.init(ftyp.TypeParams().list()) if !u.unify(ftyp, mtyp) { return m, f } @@ -373,10 +373,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // both methods must have the same number of type parameters ftyp := f.typ.(*Signature) mtyp := m.typ.(*Signature) - if ftyp.TParams().Len() != mtyp.TParams().Len() { + if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() { return m, f } - if !acceptMethodTypeParams && ftyp.TParams().Len() > 0 { + if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 { panic("method with type parameters") } @@ -387,7 +387,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // In order to compare the signatures, substitute the receiver // type parameters of ftyp with V's instantiation type arguments. // This lazily instantiates the signature of method f. - if Vn != nil && Vn.TParams().Len() > 0 { + if Vn != nil && Vn.TypeParams().Len() > 0 { // Be careful: The number of type arguments may not match // the number of receiver parameters. If so, an error was // reported earlier but the length discrepancy is still @@ -406,7 +406,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) u := newUnifier(true) - if ftyp.TParams().Len() > 0 { + if ftyp.TypeParams().Len() > 0 { // We reach here only if we accept method type parameters. // In this case, unification must consider any receiver // and method type parameters as "free" type parameters. @@ -416,7 +416,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // unimplemented call so that we test this code if we // enable method type parameters. unimplemented() - u.x.init(append(ftyp.RParams().list(), ftyp.TParams().list()...)) + u.x.init(append(ftyp.RParams().list(), ftyp.TypeParams().list()...)) } else { u.x.init(ftyp.RParams().list()) } diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index c096c1b30b..eb1ecd9595 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -12,15 +12,15 @@ import ( // A Named represents a named (defined) type. type Named struct { check *Checker - info typeInfo // for cycle detection - obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types - orig *Named // original, uninstantiated type - fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) - underlying Type // possibly a *Named during setup; never a *Named once set up completely - instPos *syntax.Pos // position information for lazy instantiation, or nil - tparams *TParamList // type parameters, or nil - targs *TypeList // type arguments (after instantiation), or nil - methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily + info typeInfo // for cycle detection + obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types + orig *Named // original, uninstantiated type + fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) + underlying Type // possibly a *Named during setup; never a *Named once set up completely + instPos *syntax.Pos // position information for lazy instantiation, or nil + tparams *TypeParamList // type parameters, or nil + targs *TypeList // type arguments (after instantiation), or nil + methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily resolve func(*Named) ([]*TypeParam, Type, []*Func) once sync.Once @@ -58,10 +58,10 @@ func (t *Named) load() *Named { // (necessary because types2 expects the receiver type for methods // on defined interface types to be the Named rather than the // underlying Interface), maybe it should just handle calling - // SetTParams, SetUnderlying, and AddMethod instead? Those + // SetTypeParams, SetUnderlying, and AddMethod instead? Those // methods would need to support reentrant calls though. It would // also make the API more future-proof towards further extensions - // (like SetTParams). + // (like SetTypeParams). tparams, underlying, methods := t.resolve(t) @@ -78,7 +78,7 @@ func (t *Named) load() *Named { } // newNamed is like NewNamed but with a *Checker receiver and additional orig argument. -func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TParamList, methods []*Func) *Named { +func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods []*Func) *Named { typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods} if typ.orig == nil { typ.orig = typ @@ -119,15 +119,15 @@ func (t *Named) Orig() *Named { return t.orig } // TODO(gri) Come up with a better representation and API to distinguish // between parameterized instantiated and non-instantiated types. -// TParams returns the type parameters of the named type t, or nil. +// TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) TParams() *TParamList { return t.load().tparams } +func (t *Named) TypeParams() *TypeParamList { return t.load().tparams } -// SetTParams sets the type parameters of the named type t. -func (t *Named) SetTParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } +// SetTypeParams sets the type parameters of the named type t. +func (t *Named) SetTypeParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } -// TArgs returns the type arguments used to instantiate the named type t. -func (t *Named) TArgs() *TypeList { return t.targs } +// TypeArgs returns the type arguments used to instantiate the named type t. +func (t *Named) TypeArgs() *TypeList { return t.targs } // NumMethods returns the number of explicit methods whose receiver is named type t. func (t *Named) NumMethods() int { return len(t.load().methods) } @@ -245,8 +245,8 @@ func (n *Named) setUnderlying(typ Type) { func (n *Named) expand(env *Environment) *Named { if n.instPos != nil { // n must be loaded before instantiation, in order to have accurate - // tparams. This is done implicitly by the call to n.TParams, but making it - // explicit is harmless: load is idempotent. + // tparams. This is done implicitly by the call to n.TypeParams, but making + // it explicit is harmless: load is idempotent. n.load() var u Type if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { @@ -268,7 +268,7 @@ func (n *Named) expand(env *Environment) *Named { // shouldn't return that instance from expand. env.typeForHash(h, n) } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TParams().list(), n.targs.list()), env) + u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TypeParams().list(), n.targs.list()), env) } else { u = Typ[Invalid] } diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index a3f5f913aa..9bc2e285ce 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -475,8 +475,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { if _, ok := typ.(*Basic); ok { return } - if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 { - newTypeWriter(buf, qf).tParamList(named.TParams().list()) + if named, _ := typ.(*Named); named != nil && named.TypeParams().Len() > 0 { + newTypeWriter(buf, qf).tParamList(named.TypeParams().list()) } if tname.IsAlias() { buf.WriteString(" =") diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 3ccafef990..473d22675f 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -21,7 +21,7 @@ func isNamed(typ Type) bool { func isGeneric(typ Type) bool { // A parameterized type is only instantiated if it doesn't have an instantiation already. named, _ := typ.(*Named) - return named != nil && named.obj != nil && named.targs == nil && named.TParams() != nil + return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil } func is(typ Type, what BasicInfo) bool { @@ -220,7 +220,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // parameter names. if y, ok := y.(*Signature); ok { return x.variadic == y.variadic && - identicalTParams(x.TParams().list(), y.TParams().list(), cmpTags, p) && + identicalTParams(x.TypeParams().list(), y.TypeParams().list(), cmpTags, p) && identical(x.params, y.params, cmpTags, p) && identical(x.results, y.results, cmpTags, p) } @@ -305,8 +305,8 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { x.expand(nil) y.expand(nil) - xargs := x.TArgs().list() - yargs := y.TArgs().list() + xargs := x.TypeArgs().list() + yargs := y.TypeArgs().list() if len(xargs) != len(yargs) { return false diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index a7d0db624c..eeaf1acbd6 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -19,13 +19,13 @@ type Signature struct { // and store it in the Func Object) because when type-checking a function // literal we call the general type checker which returns a general Type. // We then unpack the *Signature and use the scope for the literal body. - rparams *TParamList // receiver type parameters from left to right, or nil - tparams *TParamList // type parameters from left to right, or nil - scope *Scope // function scope, present for package-local signatures - recv *Var // nil if not a method - params *Tuple // (incoming) parameters from left to right; or nil - results *Tuple // (outgoing) results from left to right; or nil - variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) + rparams *TypeParamList // receiver type parameters from left to right, or nil + tparams *TypeParamList // type parameters from left to right, or nil + scope *Scope // function scope, present for package-local signatures + recv *Var // nil if not a method + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) } // NewSignature returns a new function type for the given receiver, parameters, @@ -53,14 +53,14 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { // contain methods whose receiver type is a different interface. func (s *Signature) Recv() *Var { return s.recv } -// TParams returns the type parameters of signature s, or nil. -func (s *Signature) TParams() *TParamList { return s.tparams } +// TypeParams returns the type parameters of signature s, or nil. +func (s *Signature) TypeParams() *TypeParamList { return s.tparams } -// SetTParams sets the type parameters of signature s. -func (s *Signature) SetTParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } +// SetTypeParams sets the type parameters of signature s. +func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } // RParams returns the receiver type parameters of signature s, or nil. -func (s *Signature) RParams() *TParamList { return s.rparams } +func (s *Signature) RParams() *TypeParamList { return s.rparams } // SetRParams sets the receiver type params of signature s. func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } @@ -133,7 +133,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] // again when we type-check the signature. // TODO(gri) maybe the receiver should be marked as invalid instead? if recv, _ := check.genericType(rname, false).(*Named); recv != nil { - recvTParams = recv.TParams().list() + recvTParams = recv.TypeParams().list() } } // provide type parameter bounds @@ -213,7 +213,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] T.expand(nil) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). - if T.TArgs() != nil && sig.RParams() == nil { + if T.TypeArgs() != nil && sig.RParams() == nil { check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ) break } diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 2032305fab..752e107e11 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -179,13 +179,13 @@ func (subst *subster) typ(typ Type) Type { } } - if t.TParams().Len() == 0 { + if t.TypeParams().Len() == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } var newTArgs []Type - assert(t.targs.Len() == t.TParams().Len()) + assert(t.targs.Len() == t.TypeParams().Len()) // already instantiated dump(">>> %s already instantiated", t) @@ -198,7 +198,7 @@ func (subst *subster) typ(typ Type) Type { if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.TParams().Len()) + newTArgs = make([]Type, t.TypeParams().Len()) copy(newTArgs, t.targs.list()) } newTArgs[i] = new_targ diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go index f313ea310e..ababe85909 100644 --- a/src/cmd/compile/internal/types2/typelists.go +++ b/src/cmd/compile/internal/types2/typelists.go @@ -6,20 +6,20 @@ package types2 import "bytes" -// TParamList holds a list of type parameters. -type TParamList struct{ tparams []*TypeParam } +// TypeParamList holds a list of type parameters. +type TypeParamList struct{ tparams []*TypeParam } // Len returns the number of type parameters in the list. // It is safe to call on a nil receiver. -func (l *TParamList) Len() int { return len(l.list()) } +func (l *TypeParamList) Len() int { return len(l.list()) } // At returns the i'th type parameter in the list. -func (l *TParamList) At(i int) *TypeParam { return l.tparams[i] } +func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] } // list is for internal use where we expect a []*TypeParam. // TODO(rfindley): list should probably be eliminated: we can pass around a -// TParamList instead. -func (l *TParamList) list() []*TypeParam { +// TypeParamList instead. +func (l *TypeParamList) list() []*TypeParam { if l == nil { return nil } @@ -66,7 +66,7 @@ func (l *TypeList) String() string { // ---------------------------------------------------------------------------- // Implementation -func bindTParams(list []*TypeParam) *TParamList { +func bindTParams(list []*TypeParam) *TypeParamList { if len(list) == 0 { return nil } @@ -76,5 +76,5 @@ func bindTParams(list []*TypeParam) *TParamList { } typ.index = i } - return &TParamList{tparams: list} + return &TypeParamList{tparams: list} } diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index e7181281af..505596f571 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -29,7 +29,7 @@ type TypeParam struct { func (t *TypeParam) Obj() *TypeName { return t.obj } // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named -// or Signature type by calling SetTParams. Setting a type parameter on more +// or Signature type by calling SetTypeParams. Setting a type parameter on more // than one type will result in a panic. // // The constraint argument can be nil, and set later via SetConstraint. diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 23fd788fbe..39ba278d53 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -237,9 +237,9 @@ func (w *typeWriter) typ(typ Type) { if t.targs != nil { // instantiated type w.typeList(t.targs.list()) - } else if w.env == nil && t.TParams().Len() != 0 { // For type hashing, don't need to format the TParams + } else if w.env == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TParams // parameterized type - w.tParamList(t.TParams().list()) + w.tParamList(t.TypeParams().list()) } case *TypeParam: @@ -358,8 +358,8 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { } func (w *typeWriter) signature(sig *Signature) { - if sig.TParams().Len() != 0 { - w.tParamList(sig.TParams().list()) + if sig.TypeParams().Len() != 0 { + w.tParamList(sig.TypeParams().list()) } w.tuple(sig.params, sig.variadic) -- cgit v1.2.3-54-g00ecf From a1f6208e56436281ce5e26ad745f8fc3f7a9b280 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 16:03:57 -0700 Subject: go/types, types2: add Environment to Config Port to types2 and adjust compiler accordingly. Change-Id: I2e72b151ef834977dca64cb2e62cedcac4e46062 Reviewed-on: https://go-review.googlesource.com/c/go/+/348578 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky Reviewed-by: Robert Findley TryBot-Result: Go Bot --- src/cmd/compile/internal/noder/import.go | 10 +++++----- src/cmd/compile/internal/noder/irgen.go | 7 ++++--- src/cmd/compile/internal/noder/reader2.go | 10 ++++------ src/cmd/compile/internal/noder/unified.go | 4 ++-- src/cmd/compile/internal/types2/api.go | 5 +++++ src/cmd/compile/internal/types2/check.go | 7 +++++-- src/cmd/compile/internal/types2/decl.go | 2 +- src/cmd/compile/internal/types2/instantiate.go | 2 +- src/cmd/compile/internal/types2/named.go | 2 +- src/cmd/compile/internal/types2/subst.go | 2 +- src/go/types/api.go | 5 +++++ src/go/types/check.go | 7 +++++-- src/go/types/decl.go | 2 +- src/go/types/instantiate.go | 2 +- src/go/types/named.go | 2 +- src/go/types/subst.go | 2 +- 16 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go index c26340c960..f13f8ca7f5 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -43,12 +43,12 @@ var haveLegacyImports = false // for an imported package by overloading writeNewExportFunc, then // that payload will be mapped into memory and passed to // newReadImportFunc. -var newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { +var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { panic("unexpected new export data payload") } type gcimports struct { - check *types2.Checker + env *types2.Environment packages map[string]*types2.Package } @@ -61,7 +61,7 @@ func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*ty panic("mode must be 0") } - _, pkg, err := readImportFile(path, typecheck.Target, m.check, m.packages) + _, pkg, err := readImportFile(path, typecheck.Target, m.env, m.packages) return pkg, err } @@ -224,7 +224,7 @@ func parseImportPath(pathLit *syntax.BasicLit) (string, error) { // readImportFile reads the import file for the given package path and // returns its types.Pkg representation. If packages is non-nil, the // types2.Package representation is also returned. -func readImportFile(path string, target *ir.Package, check *types2.Checker, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { +func readImportFile(path string, target *ir.Package, env *types2.Environment, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { path, err = resolveImportPath(path) if err != nil { return @@ -279,7 +279,7 @@ func readImportFile(path string, target *ir.Package, check *types2.Checker, pack return } - pkg2, err = newReadImportFunc(data, pkg1, check, packages) + pkg2, err = newReadImportFunc(data, pkg1, env, packages) } else { // We only have old data. Oh well, fall back to the legacy importers. haveLegacyImports = true diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index a67b3994da..414875615f 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -34,10 +34,13 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) { } // typechecking + env := types2.NewEnvironment() importer := gcimports{ + env: env, packages: map[string]*types2.Package{"unsafe": types2.Unsafe}, } conf := types2.Config{ + Environment: env, GoVersion: base.Flag.Lang, IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode CompilerErrorMessages: true, // use error strings matching existing compiler errors @@ -60,9 +63,7 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) { // expand as needed } - pkg := types2.NewPackage(base.Ctxt.Pkgpath, "") - importer.check = types2.NewChecker(&conf, pkg, info) - err := importer.check.Files(files) + pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info) base.ExitIfErrors() if err != nil { diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 3886d571b5..6e2d1f2e76 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -18,7 +18,7 @@ import ( type pkgReader2 struct { pkgDecoder - check *types2.Checker + env *types2.Environment imports map[string]*types2.Package posBases []*syntax.PosBase @@ -26,11 +26,11 @@ type pkgReader2 struct { typs []types2.Type } -func readPackage2(check *types2.Checker, imports map[string]*types2.Package, input pkgDecoder) *types2.Package { +func readPackage2(env *types2.Environment, imports map[string]*types2.Package, input pkgDecoder) *types2.Package { pr := pkgReader2{ pkgDecoder: input, - check: check, + env: env, imports: imports, posBases: make([]*syntax.PosBase, input.numElems(relocPosBase)), @@ -233,9 +233,7 @@ func (r *reader2) doTyp() (res types2.Type) { obj, targs := r.obj() name := obj.(*types2.TypeName) if len(targs) != 0 { - // TODO(mdempsky) should use a single shared environment here - // (before, this used a shared checker) - t, _ := types2.Instantiate(types2.NewEnvironment(), name.Type(), targs, false) + t, _ := types2.Instantiate(r.p.env, name.Type(), targs, false) return t } return name.Type() diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index bf63608bf1..02f64d00ac 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -78,12 +78,12 @@ func unified(noders []*noder) { base.Errorf("cannot use -G and -d=quirksmode together") } - newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { + newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) { pr := newPkgDecoder(pkg1.Path, data) // Read package descriptors for both types2 and compiler backend. readPackage(newPkgReader(pr), pkg1) - pkg2 = readPackage2(check, packages, pr) + pkg2 = readPackage2(env, packages, pr) return } diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index b2938b84da..6914e6c89f 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -108,6 +108,11 @@ type ImporterFrom interface { // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { + // Environment is the environment used for resolving global + // identifiers. If nil, the type checker will initialize this + // field with a newly created environment. + Environment *Environment + // GoVersion describes the accepted Go language version. The string // must follow the format "go%d.%d" (e.g. "go1.12") or ist must be // empty; an empty string indicates the latest language version. diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index c7b45d86d1..24a05e6b37 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -86,7 +86,6 @@ type Checker struct { nextID uint64 // unique Id for type parameters (first valid Id is 1) objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - env *Environment // for deduplicating identical instances // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -171,6 +170,11 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { conf = new(Config) } + // make sure we have an environment + if conf.Environment == nil { + conf.Environment = NewEnvironment() + } + // make sure we have an info struct if info == nil { info = new(Info) @@ -188,7 +192,6 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker { version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - env: NewEnvironment(), } } diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 4181be9fa8..905c21426c 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.env) + t.expand(check.conf.Environment) // don't touch the type if it is from a different package or the Universe scope // (doing so would lead to a race condition - was issue #35049) diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 3ea21f921b..469ceea5c4 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -71,7 +71,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis }() } - inst := check.instance(pos, typ, targs, check.env) + inst := check.instance(pos, typ, targs, check.conf.Environment) assert(len(posList) <= len(targs)) check.later(func() { diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index eb1ecd9595..99410aedfb 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -254,7 +254,7 @@ func (n *Named) expand(env *Environment) *Named { // in subst) feels overly complicated. Can we simplify? if env == nil { if n.check != nil { - env = n.check.env + env = n.check.conf.Environment } else { // If we're instantiating lazily, we might be outside the scope of a // type-checking pass. In that case we won't have a pre-existing diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 752e107e11..4627dd3c5b 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -59,7 +59,7 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Enviro if check != nil { subst.check = check if env == nil { - env = check.env + env = check.conf.Environment } } if env == nil { diff --git a/src/go/types/api.go b/src/go/types/api.go index 5beeff530c..ebc3a01266 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -115,6 +115,11 @@ type ImporterFrom interface { // A Config specifies the configuration for type checking. // The zero value for Config is a ready-to-use default configuration. type Config struct { + // Environment is the environment used for resolving global + // identifiers. If nil, the type checker will initialize this + // field with a newly created environment. + Environment *Environment + // GoVersion describes the accepted Go language version. The string // must follow the format "go%d.%d" (e.g. "go1.12") or it must be // empty; an empty string indicates the latest language version. diff --git a/src/go/types/check.go b/src/go/types/check.go index 0383a58c64..63f4cbd4a0 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -89,7 +89,6 @@ type Checker struct { nextID uint64 // unique Id for type parameters (first valid Id is 1) objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package - env *Environment // for deduplicating identical instances // pkgPathMap maps package names to the set of distinct import paths we've // seen for that name, anywhere in the import graph. It is used for @@ -174,6 +173,11 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch conf = new(Config) } + // make sure we have an environment + if conf.Environment == nil { + conf.Environment = NewEnvironment() + } + // make sure we have an info struct if info == nil { info = new(Info) @@ -192,7 +196,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch version: version, objMap: make(map[Object]*declInfo), impMap: make(map[importKey]*Package), - env: NewEnvironment(), } } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index d132d30b9d..f679c33a94 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -316,7 +316,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.env) + t.expand(check.conf.Environment) // don't touch the type if it is from a different package or the Universe scope // (doing so would lead to a race condition - was issue #35049) if t.obj.pkg != check.pkg { diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 040877829c..50be07b8fd 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -71,7 +71,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, posList }() } - inst := check.instance(pos, typ, targs, check.env) + inst := check.instance(pos, typ, targs, check.conf.Environment) assert(len(posList) <= len(targs)) check.later(func() { diff --git a/src/go/types/named.go b/src/go/types/named.go index 51c4a236da..74681ab2d4 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -254,7 +254,7 @@ func (n *Named) expand(env *Environment) *Named { // in subst) feels overly complicated. Can we simplify? if env == nil { if n.check != nil { - env = n.check.env + env = n.check.conf.Environment } else { // If we're instantiating lazily, we might be outside the scope of a // type-checking pass. In that case we won't have a pre-existing diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 4f9d76d598..07fe6a6b6e 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -62,7 +62,7 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, env *Environ if check != nil { subst.check = check if env == nil { - env = check.env + env = check.conf.Environment } } if env == nil { -- cgit v1.2.3-54-g00ecf From e30a09013b24853cbe6d3d3a919e639df0bdf41c Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 8 Sep 2021 15:59:11 -0700 Subject: cmd/compile: extrapolate $GOROOT in unified IR This ensures that diagnostics for files within $GOROOT continue to be reported using their full filepath, rather than the abbreviated filepath. Notably, this is necessary for test/run.go, which has tests that expect to see the full filepath. Updates #48247. Change-Id: I440e2c6dd6109ca059d81cee49e476bba805d703 Reviewed-on: https://go-review.googlesource.com/c/go/+/348670 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky Reviewed-by: Robert Griesemer TryBot-Result: Go Bot --- src/cmd/compile/internal/noder/reader.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 204d25bce8..b3cb10dadb 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -10,6 +10,7 @@ import ( "bytes" "fmt" "go/constant" + "internal/buildcfg" "strings" "cmd/compile/internal/base" @@ -194,15 +195,32 @@ func (pr *pkgReader) posBaseIdx(idx int) *src.PosBase { r := pr.newReader(relocPosBase, idx, syncPosBase) var b *src.PosBase - filename := r.string() + absFilename := r.string() + filename := absFilename + + // For build artifact stability, the export data format only + // contains the "absolute" filename as returned by objabi.AbsFile. + // However, some tests (e.g., test/run.go's asmcheck tests) expect + // to see the full, original filename printed out. Re-expanding + // "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to + // satisfy this. + // + // TODO(mdempsky): De-duplicate this logic with similar logic in + // cmd/link/internal/ld's expandGoroot. However, this will probably + // require being more consistent about when we use native vs UNIX + // file paths. + const dollarGOROOT = "$GOROOT" + if strings.HasPrefix(filename, dollarGOROOT) { + filename = buildcfg.GOROOT + filename[len(dollarGOROOT):] + } if r.bool() { - b = src.NewFileBase(filename, filename) + b = src.NewFileBase(filename, absFilename) } else { pos := r.pos0() line := r.uint() col := r.uint() - b = src.NewLinePragmaBase(pos, filename, filename, line, col) + b = src.NewLinePragmaBase(pos, filename, absFilename, line, col) } pr.posBases[idx] = b -- cgit v1.2.3-54-g00ecf From 4c52eac49b7e3f2a107419583012e5251ccbfde9 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 7 Sep 2021 13:23:08 -0700 Subject: cmd/compile: simplify value coding for unified IR In indexed export, values are always exported along with their type and are encoded in a type-sensitive manner, because this matches how cmd/compile handled constants internally. However, go/types intentionally differs from this, decoupling type from value representation. As unified IR strives to be more go/types-centric, it makes sense to embrace this and make values a more first-class encoding. Change-Id: If21d849c4f610358bd776d5665469d180bcd5f6e Reviewed-on: https://go-review.googlesource.com/c/go/+/348014 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/noder/decoder.go | 3 ++- src/cmd/compile/internal/noder/encoder.go | 3 ++- src/cmd/compile/internal/noder/reader.go | 12 ++++-------- src/cmd/compile/internal/noder/reader2.go | 10 ++-------- src/cmd/compile/internal/noder/writer.go | 12 ++++-------- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/cmd/compile/internal/noder/decoder.go b/src/cmd/compile/internal/noder/decoder.go index 3dc61c6a69..2c18727420 100644 --- a/src/cmd/compile/internal/noder/decoder.go +++ b/src/cmd/compile/internal/noder/decoder.go @@ -255,7 +255,8 @@ func (r *decoder) strings() []string { return res } -func (r *decoder) rawValue() constant.Value { +func (r *decoder) value() constant.Value { + r.sync(syncValue) isComplex := r.bool() val := r.scalar() if isComplex { diff --git a/src/cmd/compile/internal/noder/encoder.go b/src/cmd/compile/internal/noder/encoder.go index d8ab0f6255..b07b3a4a48 100644 --- a/src/cmd/compile/internal/noder/encoder.go +++ b/src/cmd/compile/internal/noder/encoder.go @@ -237,7 +237,8 @@ func (w *encoder) strings(ss []string) { } } -func (w *encoder) rawValue(val constant.Value) { +func (w *encoder) value(val constant.Value) { + w.sync(syncValue) if w.bool(val.Kind() == constant.Complex) { w.scalar(constant.Real(val)) w.scalar(constant.Imag(val)) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index b3cb10dadb..e235dd5792 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -626,7 +626,8 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node case objConst: name := do(ir.OLITERAL, false) - typ, val := r.value() + typ := r.typ() + val := FixValue(typ, r.value()) setType(name, typ) setValue(name, val) return name @@ -755,12 +756,6 @@ func (r *reader) typeParamNames() { } } -func (r *reader) value() (*types.Type, constant.Value) { - r.sync(syncValue) - typ := r.typ() - return typ, FixValue(typ, r.rawValue()) -} - func (r *reader) method() *types.Field { r.sync(syncMethod) pos := r.pos() @@ -1556,7 +1551,8 @@ func (r *reader) expr() (res ir.Node) { case exprConst: pos := r.pos() - typ, val := r.value() + typ := r.typ() + val := FixValue(typ, r.value()) op := r.op() orig := r.string() return typecheck.Expr(OrigConst(pos, typ, val, op, orig)) diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 6e2d1f2e76..0cfde24b58 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -7,8 +7,6 @@ package noder import ( - "go/constant" - "cmd/compile/internal/base" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" @@ -388,7 +386,8 @@ func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) { case objConst: pos := r.pos() - typ, val := r.value() + typ := r.typ() + val := r.value() return types2.NewConst(pos, objPkg, objName, typ, val) case objFunc: @@ -428,11 +427,6 @@ func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) { return objPkg, objName } -func (r *reader2) value() (types2.Type, constant.Value) { - r.sync(syncValue) - return r.typ(), r.rawValue() -} - func (pr *pkgReader2) objDictIdx(idx int) *reader2Dict { r := pr.newReader(relocObjDict, idx, syncObject1) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index d1e5605739..694035b73f 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -542,7 +542,8 @@ func (w *writer) doObj(obj types2.Object) codeObj { case *types2.Const: w.pos(obj) - w.value(obj.Type(), obj.Val()) + w.typ(obj.Type()) + w.value(obj.Val()) return objConst case *types2.Func: @@ -598,12 +599,6 @@ func (w *writer) typExpr(expr syntax.Expr) { w.typ(tv.Type) } -func (w *writer) value(typ types2.Type, val constant.Value) { - w.sync(syncValue) - w.typ(typ) - w.rawValue(val) -} - // objDict writes the dictionary needed for reading the given object. func (w *writer) objDict(obj types2.Object, dict *writerDict) { // TODO(mdempsky): Split objDict into multiple entries? reader.go @@ -1199,7 +1194,8 @@ func (w *writer) expr(expr syntax.Expr) { w.code(exprConst) w.pos(pos) - w.value(tv.Type, tv.Value) + w.typ(tv.Type) + w.value(tv.Value) // TODO(mdempsky): These details are only important for backend // diagnostics. Explore writing them out separately. -- cgit v1.2.3-54-g00ecf From 42563f89d7093ab22e1ac42351e66c4d03e0c80e Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Tue, 7 Sep 2021 17:24:50 -0700 Subject: cmd/compile: remove 'ext' fields from unified IR reader/writer types This is a vestigial artifact of how I initially split apart the public and private data for objects. But now objects are split into more parts, and it's proven easier to just keep them as separate variables. So it's time to cleanup the initial public/private code to follow the same approach. Change-Id: I3976b19fb433cbe21d299d3799ec616f9e59561e Reviewed-on: https://go-review.googlesource.com/c/go/+/348412 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/noder/reader.go | 18 ++++++++--------- src/cmd/compile/internal/noder/unified.go | 2 -- src/cmd/compile/internal/noder/writer.go | 32 ++++++++++++------------------- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index e235dd5792..57e8476099 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -79,8 +79,6 @@ type reader struct { p *pkgReader - ext *reader - dict *readerDict // TODO(mdempsky): The state below is all specific to reading @@ -586,10 +584,10 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node dict := pr.objDictIdx(sym, idx, implicits, explicits) r := pr.newReader(relocObj, idx, syncObject1) - r.ext = pr.newReader(relocObjExt, idx, syncObject1) + rext := pr.newReader(relocObjExt, idx, syncObject1) r.dict = dict - r.ext.dict = dict + rext.dict = dict sym = r.mangle(sym) if !sym.IsBlank() && sym.Def != nil { @@ -642,7 +640,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node name.Func = ir.NewFunc(r.pos()) name.Func.Nname = name - r.ext.funcExt(name) + rext.funcExt(name) return name case objType: @@ -651,7 +649,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node setType(name, typ) // Important: We need to do this before SetUnderlying. - r.ext.typeExt(name) + rext.typeExt(name) // We need to defer CheckSize until we've called SetUnderlying to // handle recursive types. @@ -661,7 +659,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node methods := make([]*types.Field, r.len()) for i := range methods { - methods[i] = r.method() + methods[i] = r.method(rext) } if len(methods) != 0 { typ.Methods().Set(methods) @@ -674,7 +672,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node case objVar: name := do(ir.ONAME, false) setType(name, r.typ()) - r.ext.varExt(name) + rext.varExt(name) return name } } @@ -756,7 +754,7 @@ func (r *reader) typeParamNames() { } } -func (r *reader) method() *types.Field { +func (r *reader) method(rext *reader) *types.Field { r.sync(syncMethod) pos := r.pos() pkg, sym := r.selector() @@ -772,7 +770,7 @@ func (r *reader) method() *types.Field { name.Func = ir.NewFunc(r.pos()) name.Func.Nname = name - r.ext.funcExt(name) + rext.funcExt(name) meth := types.NewField(name.Func.Pos(), sym, typ) meth.Nname = name diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index 02f64d00ac..eff2eeaeff 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -106,7 +106,6 @@ func unified(noders []*noder) { readPackage(localPkgReader, types.LocalPkg) r := localPkgReader.newReader(relocMeta, privateRootIdx, syncPrivate) - r.ext = r r.pkgInit(types.LocalPkg, target) // Type-check any top-level assignments. We ignore non-assignments @@ -190,7 +189,6 @@ func writePkgStub(noders []*noder) string { { w := privateRootWriter - w.ext = w w.pkgInit(noders) w.flush() } diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 694035b73f..e1413da1d8 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -75,14 +75,6 @@ type writer struct { encoder - // For writing out object descriptions, ext points to the extension - // writer for where we can write the compiler's private extension - // details for the object. - // - // TODO(mdempsky): This is a little hacky, but works easiest with - // the way things are currently. - ext *writer - // TODO(mdempsky): We should be able to prune localsIdx whenever a // scope closes, and then maybe we can just use the same map for // storing the TypeParams too (as their TypeName instead). @@ -504,21 +496,21 @@ func (pw *pkgWriter) objIdx(obj types2.Object) int { } w := pw.newWriter(relocObj, syncObject1) - w.ext = pw.newWriter(relocObjExt, syncObject1) + wext := pw.newWriter(relocObjExt, syncObject1) wname := pw.newWriter(relocName, syncObject1) wdict := pw.newWriter(relocObjDict, syncObject1) pw.globalsIdx[obj] = w.idx // break cycles - assert(w.ext.idx == w.idx) + assert(wext.idx == w.idx) assert(wname.idx == w.idx) assert(wdict.idx == w.idx) w.dict = dict - w.ext.dict = dict + wext.dict = dict - code := w.doObj(obj) + code := w.doObj(wext, obj) w.flush() - w.ext.flush() + wext.flush() wname.qualifiedIdent(obj) wname.code(code) @@ -530,7 +522,7 @@ func (pw *pkgWriter) objIdx(obj types2.Object) int { return w.idx } -func (w *writer) doObj(obj types2.Object) codeObj { +func (w *writer) doObj(wext *writer, obj types2.Object) codeObj { if obj.Pkg() != w.p.curpkg { return objStub } @@ -555,7 +547,7 @@ func (w *writer) doObj(obj types2.Object) codeObj { w.typeParamNames(sig.TypeParams()) w.signature(sig) w.pos(decl) - w.ext.funcExt(obj) + wext.funcExt(obj) return objFunc case *types2.TypeName: @@ -573,12 +565,12 @@ func (w *writer) doObj(obj types2.Object) codeObj { w.pos(obj) w.typeParamNames(named.TypeParams()) - w.ext.typeExt(obj) + wext.typeExt(obj) w.typExpr(decl.Type) w.len(named.NumMethods()) for i := 0; i < named.NumMethods(); i++ { - w.method(named.Method(i)) + w.method(wext, named.Method(i)) } return objType @@ -586,7 +578,7 @@ func (w *writer) doObj(obj types2.Object) codeObj { case *types2.Var: w.pos(obj) w.typ(obj.Type()) - w.ext.varExt(obj) + wext.varExt(obj) return objVar } } @@ -648,7 +640,7 @@ func (w *writer) typeParamNames(tparams *types2.TypeParamList) { } } -func (w *writer) method(meth *types2.Func) { +func (w *writer) method(wext *writer, meth *types2.Func) { decl, ok := w.p.funDecls[meth] assert(ok) sig := meth.Type().(*types2.Signature) @@ -661,7 +653,7 @@ func (w *writer) method(meth *types2.Func) { w.signature(sig) w.pos(decl) // XXX: Hack to workaround linker limitations. - w.ext.funcExt(meth) + wext.funcExt(meth) } // qualifiedIdent writes out the name of an object declared at package -- cgit v1.2.3-54-g00ecf From 9cbc76bdf90752e21a2b3f05944552e1373ab433 Mon Sep 17 00:00:00 2001 From: fanzha02 Date: Tue, 24 Aug 2021 12:46:54 +0800 Subject: cmd/internal/obj/arm64: add checks for incorrect use of REGTMP register The current assembler uses REGTMP as a temporary destination register, when optimizing one instruction into a multi-instruction sequence. But in some cases, when the source register is REGTMP, this behavior is incorrect. For example: ADD $0x1234567, R27, R3 The current assembler encodes it into MOVD $17767, R27 MOVK $(291<<16), R27 ADD R27, R27, R3 It is illegal to overwrite R27. This CL adds the related checks. Add test cases. Change-Id: I0af373d9fd23d8f067c093778dd4cc76748faf38 Reviewed-on: https://go-review.googlesource.com/c/go/+/344689 Reviewed-by: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Trust: fannie zhang --- src/cmd/asm/internal/asm/testdata/arm64error.s | 21 ++++++++++++++------- src/cmd/internal/obj/arm64/asm7.go | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s index 8b12b16680..7b006432c0 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64error.s +++ b/src/cmd/asm/internal/asm/testdata/arm64error.s @@ -406,12 +406,12 @@ TEXT errors(SB),$0 VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement" VUADDW V9.B8, V12.H8, V14.B8 // ERROR "invalid arrangement" VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch" - VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" - VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" + VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" + VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement" VUMAX V1.B8, V2.B8, V3.B16 // ERROR "operand mismatch" VUMIN V1.H4, V2.S4, V3.H4 // ERROR "operand mismatch" VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range" - VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range" + VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range" CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register" CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register" CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous" @@ -419,8 +419,15 @@ TEXT errors(SB),$0 ADD R1>>2, RSP, R3 // ERROR "illegal combination" ADDS R2<<3, R3, RSP // ERROR "unexpected SP reference" CMP R1<<5, RSP // ERROR "the left shift amount out of range 0 to 4" - MOVD.P y+8(FP), R1 // ERROR "illegal combination" - MOVD.W x-8(SP), R1 // ERROR "illegal combination" - LDP.P x+8(FP), (R0, R1) // ERROR "illegal combination" - LDP.W x+8(SP), (R0, R1) // ERROR "illegal combination" + MOVD.P y+8(FP), R1 // ERROR "illegal combination" + MOVD.W x-8(SP), R1 // ERROR "illegal combination" + LDP.P x+8(FP), (R0, R1) // ERROR "illegal combination" + LDP.W x+8(SP), (R0, R1) // ERROR "illegal combination" + ADD $0x1234567, R27, R3 // ERROR "cannot use REGTMP as source" + ADD $0x3fffffffc000, R27, R5 // ERROR "cannot use REGTMP as source" + AND $0x22220000, R27, R4 // ERROR "cannot use REGTMP as source" + ANDW $0x6006000060060, R27, R5 // ERROR "cannot use REGTMP as source" + STP (R3, R4), 0x1234567(R27) // ERROR "REGTMP used in large offset store" + LDP 0x1234567(R27), (R3, R4) // ERROR "REGTMP used in large offset load" + STP (R26, R27), 700(R2) // ERROR "cannot use REGTMP as source" RET diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 8db25cf967..5d6caaed5f 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -3407,6 +3407,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o4 = os[3] case 13: /* addop $vcon, [R], R (64 bit literal); cmp $lcon,R -> addop $lcon,R, ZR */ + if p.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } if p.To.Reg == REG_RSP && isADDSop(p.As) { c.ctxt.Diag("illegal destination register: %v\n", p) } @@ -3724,6 +3727,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= (uint32(r&31) << 5) | uint32(rt&31) case 28: /* logop $vcon, [R], R (64 bit literal) */ + if p.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } o := uint32(0) num := uint8(0) cls := oclass(&p.From) @@ -4354,6 +4360,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { /* reloc ops */ case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */ + if p.From.Reg == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v\n", p) + } o1 = ADR(1, 0, REGTMP) o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31 rel := obj.Addrel(c.cursym) @@ -4585,6 +4594,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { // add Rtmp, R, Rtmp // ldp (Rtmp), (R1, R2) r := int(p.From.Reg) + if r == REGTMP { + c.ctxt.Diag("REGTMP used in large offset load: %v", p) + } if r == obj.REG_NONE { r = int(o.param) } @@ -4601,6 +4613,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { case 76: // add $O, R, Rtmp or sub $O, R, Rtmp // stp (R1, R2), (Rtmp) + if p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v", p) + } r := int(p.To.Reg) if r == obj.REG_NONE { r = int(o.param) @@ -4628,6 +4643,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { // add Rtmp, R, Rtmp // stp (R1, R2), (Rtmp) r := int(p.To.Reg) + if r == REGTMP || p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("REGTMP used in large offset store: %v", p) + } if r == obj.REG_NONE { r = int(o.param) } @@ -4933,6 +4951,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) { o1 |= (uint32(Q&1) << 30) | (uint32((r>>5)&7) << 16) | (uint32(r&0x1f) << 5) | uint32(rt&31) case 87: /* stp (r,r), addr(SB) -> adrp + add + stp */ + if p.From.Reg == REGTMP || p.From.Offset == REGTMP { + c.ctxt.Diag("cannot use REGTMP as source: %v", p) + } o1 = ADR(1, 0, REGTMP) o2 = c.opirr(p, AADD) | REGTMP&31<<5 | REGTMP&31 rel := obj.Addrel(c.cursym) -- cgit v1.2.3-54-g00ecf From 8fad81cd6294776c63a3e9a5820f196949974861 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 28 Aug 2021 16:40:34 +0700 Subject: cmd/compile: fold handling OCONV logic to separate function So next CL can re-use the logic to perform checkptr instrumentation. Change-Id: I3241e9c3c84da04db71fd1d4fd83cb76b2e18521 Reviewed-on: https://go-review.googlesource.com/c/go/+/345435 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/ssagen/ssa.go | 338 +++++++++++++++++---------------- 1 file changed, 170 insertions(+), 168 deletions(-) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 1d5a872b1b..dd19a254f8 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -2323,6 +2323,175 @@ func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op { return x } +func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value { + if ft.IsBoolean() && tt.IsKind(types.TUINT8) { + // Bool -> uint8 is generated internally when indexing into runtime.staticbyte. + return s.newValue1(ssa.OpCopy, tt, v) + } + if ft.IsInteger() && tt.IsInteger() { + var op ssa.Op + if tt.Size() == ft.Size() { + op = ssa.OpCopy + } else if tt.Size() < ft.Size() { + // truncation + switch 10*ft.Size() + tt.Size() { + case 21: + op = ssa.OpTrunc16to8 + case 41: + op = ssa.OpTrunc32to8 + case 42: + op = ssa.OpTrunc32to16 + case 81: + op = ssa.OpTrunc64to8 + case 82: + op = ssa.OpTrunc64to16 + case 84: + op = ssa.OpTrunc64to32 + default: + s.Fatalf("weird integer truncation %v -> %v", ft, tt) + } + } else if ft.IsSigned() { + // sign extension + switch 10*ft.Size() + tt.Size() { + case 12: + op = ssa.OpSignExt8to16 + case 14: + op = ssa.OpSignExt8to32 + case 18: + op = ssa.OpSignExt8to64 + case 24: + op = ssa.OpSignExt16to32 + case 28: + op = ssa.OpSignExt16to64 + case 48: + op = ssa.OpSignExt32to64 + default: + s.Fatalf("bad integer sign extension %v -> %v", ft, tt) + } + } else { + // zero extension + switch 10*ft.Size() + tt.Size() { + case 12: + op = ssa.OpZeroExt8to16 + case 14: + op = ssa.OpZeroExt8to32 + case 18: + op = ssa.OpZeroExt8to64 + case 24: + op = ssa.OpZeroExt16to32 + case 28: + op = ssa.OpZeroExt16to64 + case 48: + op = ssa.OpZeroExt32to64 + default: + s.Fatalf("weird integer sign extension %v -> %v", ft, tt) + } + } + return s.newValue1(op, tt, v) + } + + if ft.IsFloat() || tt.IsFloat() { + conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}] + if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat { + if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { + conv = conv1 + } + } + if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat { + if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { + conv = conv1 + } + } + + if Arch.LinkArch.Family == sys.MIPS && !s.softFloat { + if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() { + // tt is float32 or float64, and ft is also unsigned + if tt.Size() == 4 { + return s.uint32Tofloat32(n, v, ft, tt) + } + if tt.Size() == 8 { + return s.uint32Tofloat64(n, v, ft, tt) + } + } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() { + // ft is float32 or float64, and tt is unsigned integer + if ft.Size() == 4 { + return s.float32ToUint32(n, v, ft, tt) + } + if ft.Size() == 8 { + return s.float64ToUint32(n, v, ft, tt) + } + } + } + + if !ok { + s.Fatalf("weird float conversion %v -> %v", ft, tt) + } + op1, op2, it := conv.op1, conv.op2, conv.intermediateType + + if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid { + // normal case, not tripping over unsigned 64 + if op1 == ssa.OpCopy { + if op2 == ssa.OpCopy { + return v + } + return s.newValueOrSfCall1(op2, tt, v) + } + if op2 == ssa.OpCopy { + return s.newValueOrSfCall1(op1, tt, v) + } + return s.newValueOrSfCall1(op2, tt, s.newValueOrSfCall1(op1, types.Types[it], v)) + } + // Tricky 64-bit unsigned cases. + if ft.IsInteger() { + // tt is float32 or float64, and ft is also unsigned + if tt.Size() == 4 { + return s.uint64Tofloat32(n, v, ft, tt) + } + if tt.Size() == 8 { + return s.uint64Tofloat64(n, v, ft, tt) + } + s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt) + } + // ft is float32 or float64, and tt is unsigned integer + if ft.Size() == 4 { + return s.float32ToUint64(n, v, ft, tt) + } + if ft.Size() == 8 { + return s.float64ToUint64(n, v, ft, tt) + } + s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt) + return nil + } + + if ft.IsComplex() && tt.IsComplex() { + var op ssa.Op + if ft.Size() == tt.Size() { + switch ft.Size() { + case 8: + op = ssa.OpRound32F + case 16: + op = ssa.OpRound64F + default: + s.Fatalf("weird complex conversion %v -> %v", ft, tt) + } + } else if ft.Size() == 8 && tt.Size() == 16 { + op = ssa.OpCvt32Fto64F + } else if ft.Size() == 16 && tt.Size() == 8 { + op = ssa.OpCvt64Fto32F + } else { + s.Fatalf("weird complex conversion %v -> %v", ft, tt) + } + ftp := types.FloatForComplex(ft) + ttp := types.FloatForComplex(tt) + return s.newValue2(ssa.OpComplexMake, tt, + s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, v)), + s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v))) + } + + s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind()) + return nil +} + // expr converts the expression n to ssa, adds it to s and returns the ssa result. func (s *state) expr(n ir.Node) *ssa.Value { if ir.HasUniquePos(n) { @@ -2510,174 +2679,7 @@ func (s *state) expr(n ir.Node) *ssa.Value { case ir.OCONV: n := n.(*ir.ConvExpr) x := s.expr(n.X) - ft := n.X.Type() // from type - tt := n.Type() // to type - if ft.IsBoolean() && tt.IsKind(types.TUINT8) { - // Bool -> uint8 is generated internally when indexing into runtime.staticbyte. - return s.newValue1(ssa.OpCopy, n.Type(), x) - } - if ft.IsInteger() && tt.IsInteger() { - var op ssa.Op - if tt.Size() == ft.Size() { - op = ssa.OpCopy - } else if tt.Size() < ft.Size() { - // truncation - switch 10*ft.Size() + tt.Size() { - case 21: - op = ssa.OpTrunc16to8 - case 41: - op = ssa.OpTrunc32to8 - case 42: - op = ssa.OpTrunc32to16 - case 81: - op = ssa.OpTrunc64to8 - case 82: - op = ssa.OpTrunc64to16 - case 84: - op = ssa.OpTrunc64to32 - default: - s.Fatalf("weird integer truncation %v -> %v", ft, tt) - } - } else if ft.IsSigned() { - // sign extension - switch 10*ft.Size() + tt.Size() { - case 12: - op = ssa.OpSignExt8to16 - case 14: - op = ssa.OpSignExt8to32 - case 18: - op = ssa.OpSignExt8to64 - case 24: - op = ssa.OpSignExt16to32 - case 28: - op = ssa.OpSignExt16to64 - case 48: - op = ssa.OpSignExt32to64 - default: - s.Fatalf("bad integer sign extension %v -> %v", ft, tt) - } - } else { - // zero extension - switch 10*ft.Size() + tt.Size() { - case 12: - op = ssa.OpZeroExt8to16 - case 14: - op = ssa.OpZeroExt8to32 - case 18: - op = ssa.OpZeroExt8to64 - case 24: - op = ssa.OpZeroExt16to32 - case 28: - op = ssa.OpZeroExt16to64 - case 48: - op = ssa.OpZeroExt32to64 - default: - s.Fatalf("weird integer sign extension %v -> %v", ft, tt) - } - } - return s.newValue1(op, n.Type(), x) - } - - if ft.IsFloat() || tt.IsFloat() { - conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}] - if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat { - if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { - conv = conv1 - } - } - if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat { - if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 { - conv = conv1 - } - } - - if Arch.LinkArch.Family == sys.MIPS && !s.softFloat { - if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() { - // tt is float32 or float64, and ft is also unsigned - if tt.Size() == 4 { - return s.uint32Tofloat32(n, x, ft, tt) - } - if tt.Size() == 8 { - return s.uint32Tofloat64(n, x, ft, tt) - } - } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() { - // ft is float32 or float64, and tt is unsigned integer - if ft.Size() == 4 { - return s.float32ToUint32(n, x, ft, tt) - } - if ft.Size() == 8 { - return s.float64ToUint32(n, x, ft, tt) - } - } - } - - if !ok { - s.Fatalf("weird float conversion %v -> %v", ft, tt) - } - op1, op2, it := conv.op1, conv.op2, conv.intermediateType - - if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid { - // normal case, not tripping over unsigned 64 - if op1 == ssa.OpCopy { - if op2 == ssa.OpCopy { - return x - } - return s.newValueOrSfCall1(op2, n.Type(), x) - } - if op2 == ssa.OpCopy { - return s.newValueOrSfCall1(op1, n.Type(), x) - } - return s.newValueOrSfCall1(op2, n.Type(), s.newValueOrSfCall1(op1, types.Types[it], x)) - } - // Tricky 64-bit unsigned cases. - if ft.IsInteger() { - // tt is float32 or float64, and ft is also unsigned - if tt.Size() == 4 { - return s.uint64Tofloat32(n, x, ft, tt) - } - if tt.Size() == 8 { - return s.uint64Tofloat64(n, x, ft, tt) - } - s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt) - } - // ft is float32 or float64, and tt is unsigned integer - if ft.Size() == 4 { - return s.float32ToUint64(n, x, ft, tt) - } - if ft.Size() == 8 { - return s.float64ToUint64(n, x, ft, tt) - } - s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt) - return nil - } - - if ft.IsComplex() && tt.IsComplex() { - var op ssa.Op - if ft.Size() == tt.Size() { - switch ft.Size() { - case 8: - op = ssa.OpRound32F - case 16: - op = ssa.OpRound64F - default: - s.Fatalf("weird complex conversion %v -> %v", ft, tt) - } - } else if ft.Size() == 8 && tt.Size() == 16 { - op = ssa.OpCvt32Fto64F - } else if ft.Size() == 16 && tt.Size() == 8 { - op = ssa.OpCvt64Fto32F - } else { - s.Fatalf("weird complex conversion %v -> %v", ft, tt) - } - ftp := types.FloatForComplex(ft) - ttp := types.FloatForComplex(tt) - return s.newValue2(ssa.OpComplexMake, tt, - s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)), - s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x))) - } - - s.Fatalf("unhandled OCONV %s -> %s", n.X.Type().Kind(), n.Type().Kind()) - return nil + return s.conv(n, x, n.X.Type(), n.Type()) case ir.ODOTTYPE: n := n.(*ir.TypeAssertExpr) -- cgit v1.2.3-54-g00ecf From d62866ef793872779c9011161e51b9c805fcb73d Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 27 Aug 2021 20:07:00 +0700 Subject: cmd/compile: move checkptr alignment to SSA generation This is followup of CL 343972, moving the checkptr alignment instrumentation during SSA generation instead of walk. Change-Id: I29b2953e4eb8631277fe2e0f44b9d987dd7a69f9 Reviewed-on: https://go-review.googlesource.com/c/go/+/345430 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/ir/expr.go | 9 +++--- src/cmd/compile/internal/ir/symtab.go | 55 ++++++++++++++++---------------- src/cmd/compile/internal/ssagen/ssa.go | 51 +++++++++++++++++++++++++---- src/cmd/compile/internal/walk/convert.go | 38 ---------------------- src/cmd/compile/internal/walk/expr.go | 13 +------- 5 files changed, 77 insertions(+), 89 deletions(-) diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go index baf0117409..f526d987a7 100644 --- a/src/cmd/compile/internal/ir/expr.go +++ b/src/cmd/compile/internal/ir/expr.go @@ -570,11 +570,10 @@ func (*SelectorExpr) CanBeNtype() {} // A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max]. type SliceExpr struct { miniExpr - X Node - Low Node - High Node - Max Node - CheckPtrCall *CallExpr `mknode:"-"` + X Node + Low Node + High Node + Max Node } func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr { diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go index 1e8261810f..1435e4313e 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -11,33 +11,34 @@ import ( // Syms holds known symbols. var Syms struct { - AssertE2I *obj.LSym - AssertE2I2 *obj.LSym - AssertI2I *obj.LSym - AssertI2I2 *obj.LSym - Deferproc *obj.LSym - DeferprocStack *obj.LSym - Deferreturn *obj.LSym - Duffcopy *obj.LSym - Duffzero *obj.LSym - GCWriteBarrier *obj.LSym - Goschedguarded *obj.LSym - Growslice *obj.LSym - Msanread *obj.LSym - Msanwrite *obj.LSym - Msanmove *obj.LSym - Newobject *obj.LSym - Newproc *obj.LSym - Panicdivide *obj.LSym - Panicshift *obj.LSym - PanicdottypeE *obj.LSym - PanicdottypeI *obj.LSym - Panicnildottype *obj.LSym - Panicoverflow *obj.LSym - Raceread *obj.LSym - Racereadrange *obj.LSym - Racewrite *obj.LSym - Racewriterange *obj.LSym + AssertE2I *obj.LSym + AssertE2I2 *obj.LSym + AssertI2I *obj.LSym + AssertI2I2 *obj.LSym + CheckPtrAlignment *obj.LSym + Deferproc *obj.LSym + DeferprocStack *obj.LSym + Deferreturn *obj.LSym + Duffcopy *obj.LSym + Duffzero *obj.LSym + GCWriteBarrier *obj.LSym + Goschedguarded *obj.LSym + Growslice *obj.LSym + Msanread *obj.LSym + Msanwrite *obj.LSym + Msanmove *obj.LSym + Newobject *obj.LSym + Newproc *obj.LSym + Panicdivide *obj.LSym + Panicshift *obj.LSym + PanicdottypeE *obj.LSym + PanicdottypeI *obj.LSym + Panicnildottype *obj.LSym + Panicoverflow *obj.LSym + Raceread *obj.LSym + Racereadrange *obj.LSym + Racewrite *obj.LSym + Racewriterange *obj.LSym // Wasm SigPanic *obj.LSym Staticuint64s *obj.LSym diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index dd19a254f8..11bca89fd8 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -96,6 +96,7 @@ func InitConfig() { ir.Syms.AssertE2I2 = typecheck.LookupRuntimeFunc("assertE2I2") ir.Syms.AssertI2I = typecheck.LookupRuntimeFunc("assertI2I") ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2") + ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment") ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc") ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack") ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn") @@ -366,6 +367,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { if fn.Pragma&ir.CgoUnsafeArgs != 0 { s.cgoUnsafeArgs = true } + s.checkPtrEnabled = ir.ShouldCheckPtr(fn, 1) fe := ssafn{ curfn: fn, @@ -709,6 +711,31 @@ func (s *state) newObject(typ *types.Type) *ssa.Value { return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0] } +func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) { + if !n.Type().IsPtr() { + s.Fatalf("expected pointer type: %v", n.Type()) + } + elem := n.Type().Elem() + if count != nil { + if !elem.IsArray() { + s.Fatalf("expected array type: %v", elem) + } + elem = elem.Elem() + } + size := elem.Size() + // Casting from larger type to smaller one is ok, so for smallest type, do nothing. + if elem.Alignment() == 1 && (size == 0 || size == 1 || count == nil) { + return + } + if count == nil { + count = s.constInt(types.Types[types.TUINTPTR], 1) + } + if count.Type.Size() != s.config.PtrSize { + s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize) + } + s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, s.reflectType(elem), count) +} + // reflectType returns an SSA value representing a pointer to typ's // reflection type descriptor. func (s *state) reflectType(typ *types.Type) *ssa.Value { @@ -861,10 +888,11 @@ type state struct { // Used to deduplicate panic calls. panics map[funcLine]*ssa.Block - cgoUnsafeArgs bool - hasdefer bool // whether the function contains a defer statement - softFloat bool - hasOpenDefers bool // whether we are doing open-coded defers + cgoUnsafeArgs bool + hasdefer bool // whether the function contains a defer statement + softFloat bool + hasOpenDefers bool // whether we are doing open-coded defers + checkPtrEnabled bool // whether to insert checkptr instrumentation // If doing open-coded defers, list of info about the defer calls in // scanning order. Hence, at exit we should run these defers in reverse @@ -2494,6 +2522,10 @@ func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value { // expr converts the expression n to ssa, adds it to s and returns the ssa result. func (s *state) expr(n ir.Node) *ssa.Value { + return s.exprCheckPtr(n, true) +} + +func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { if ir.HasUniquePos(n) { // ONAMEs and named OLITERALs have the line number // of the decl, not the use. See issue 14742. @@ -2641,6 +2673,9 @@ func (s *state) expr(n ir.Node) *ssa.Value { // unsafe.Pointer <--> *T if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() { + if s.checkPtrEnabled && checkPtrOK && to.IsPtr() && from.IsUnsafePtr() { + s.checkPtrAlignment(n, v, nil) + } return v } @@ -3081,7 +3116,8 @@ func (s *state) expr(n ir.Node) *ssa.Value { case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR: n := n.(*ir.SliceExpr) - v := s.expr(n.X) + check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr() + v := s.exprCheckPtr(n.X, !check) var i, j, k *ssa.Value if n.Low != nil { i = s.expr(n.Low) @@ -3093,8 +3129,9 @@ func (s *state) expr(n ir.Node) *ssa.Value { k = s.expr(n.Max) } p, l, c := s.slice(v, i, j, k, n.Bounded()) - if n.CheckPtrCall != nil { - s.stmt(n.CheckPtrCall) + if check { + // Emit checkptr instrumentation after bound check to prevent false positive, see #46938. + s.checkPtrAlignment(n.X.(*ir.ConvExpr), v, s.conv(n.Max, k, k.Type, types.Types[types.TUINTPTR])) } return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c) diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go index d701d545de..5d69fc3868 100644 --- a/src/cmd/compile/internal/walk/convert.go +++ b/src/cmd/compile/internal/walk/convert.go @@ -25,9 +25,6 @@ func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node { return n.X } if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) { - if n.Type().IsPtr() && n.X.Type().IsUnsafePtr() { // unsafe.Pointer to *T - return walkCheckPtrAlignment(n, init, nil) - } if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer return walkCheckPtrArithmetic(n, init) } @@ -414,41 +411,6 @@ func byteindex(n ir.Node) ir.Node { return n } -func walkCheckPtrAlignment(n *ir.ConvExpr, init *ir.Nodes, se *ir.SliceExpr) ir.Node { - if !n.Type().IsPtr() { - base.Fatalf("expected pointer type: %v", n.Type()) - } - elem := n.Type().Elem() - var count ir.Node - if se != nil { - count = se.Max - } - if count != nil { - if !elem.IsArray() { - base.Fatalf("expected array type: %v", elem) - } - elem = elem.Elem() - } - - size := elem.Size() - if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) { - return n - } - - if count == nil { - count = ir.NewInt(1) - } - - n.X = cheapExpr(n.X, init) - checkPtrCall := mkcall("checkptrAlignment", nil, init, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]), reflectdata.TypePtr(elem), typecheck.Conv(count, types.Types[types.TUINTPTR])) - if se != nil { - se.CheckPtrCall = checkPtrCall - } else { - init.Append(checkPtrCall) - } - return n -} - func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node { // Calling cheapExpr(n, init) below leads to a recursive call to // walkExpr, which leads us back here again. Use n.Checkptr to diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go index ed2d68539d..e5bf6cf0b5 100644 --- a/src/cmd/compile/internal/walk/expr.go +++ b/src/cmd/compile/internal/walk/expr.go @@ -807,15 +807,7 @@ func walkSend(n *ir.SendStmt, init *ir.Nodes) ir.Node { // walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node. func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node { - - checkSlice := ir.ShouldCheckPtr(ir.CurFunc, 1) && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr() - if checkSlice { - conv := n.X.(*ir.ConvExpr) - conv.X = walkExpr(conv.X, init) - } else { - n.X = walkExpr(n.X, init) - } - + n.X = walkExpr(n.X, init) n.Low = walkExpr(n.Low, init) if n.Low != nil && ir.IsZero(n.Low) { // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k]. @@ -823,9 +815,6 @@ func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node { } n.High = walkExpr(n.High, init) n.Max = walkExpr(n.Max, init) - if checkSlice { - n.X = walkCheckPtrAlignment(n.X.(*ir.ConvExpr), init, n) - } if n.Op().IsSlice3() { if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) { -- cgit v1.2.3-54-g00ecf From 2481f6e367a56207b6c873180e0db9bc4f2b6365 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 9 Sep 2021 01:11:26 +0700 Subject: cmd/compile: fix wrong instantiated type for embedded receiver In case of embedded field, if the receiver was fully instantiated, we must use its instantiated type, instead of passing the type params of the base receiver. Fixes #47797 Fixes #48253 Change-Id: I97613e7e669a72605137e82406f7bf5fbb629378 Reviewed-on: https://go-review.googlesource.com/c/go/+/348549 Trust: Cuong Manh Le Trust: Dan Scales Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Dan Scales --- src/cmd/compile/internal/noder/expr.go | 18 +++--------------- test/typeparam/issue47797.go | 22 ++++++++++++++++++++++ test/typeparam/issue48253.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 test/typeparam/issue47797.go create mode 100644 test/typeparam/issue48253.go diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index 5eeafddae2..045f028e1a 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -360,12 +360,10 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto n.(*ir.SelectorExpr).Selection.Nname = method typed(method.Type(), n) - // selinfo.Targs() are the types used to - // instantiate the type of receiver - targs2 := getTargs(selinfo) - targs := make([]ir.Node, targs2.Len()) + xt := deref(x.Type()) + targs := make([]ir.Node, len(xt.RParams())) for i := range targs { - targs[i] = ir.TypeNode(g.typ(targs2.At(i))) + targs[i] = ir.TypeNode(xt.RParams()[i]) } // Create function instantiation with the type @@ -388,16 +386,6 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto return n } -// getTargs gets the targs associated with the receiver of a selected method -func getTargs(selinfo *types2.Selection) *types2.TypeList { - r := deref2(selinfo.Recv()) - n := types2.AsNamed(r) - if n == nil { - base.Fatalf("Incorrect type for selinfo %v", selinfo) - } - return n.TypeArgs() -} - func (g *irgen) exprList(expr syntax.Expr) []ir.Node { return g.exprs(unpackListExpr(expr)) } diff --git a/test/typeparam/issue47797.go b/test/typeparam/issue47797.go new file mode 100644 index 0000000000..3e80d3c7a9 --- /dev/null +++ b/test/typeparam/issue47797.go @@ -0,0 +1,22 @@ +// compile -G=3 + +// Copyright 2021 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 p + +type Foo[T any] struct { + Val T +} + +func (f Foo[T]) Bat() {} + +type Bar struct { + Foo[int] +} + +func foo() { + var b Bar + b.Bat() +} diff --git a/test/typeparam/issue48253.go b/test/typeparam/issue48253.go new file mode 100644 index 0000000000..7bd0234e57 --- /dev/null +++ b/test/typeparam/issue48253.go @@ -0,0 +1,34 @@ +// run -gcflags="-G=3" + +// Copyright 2021 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 main + +import ( + "reflect" +) + +type A[T any] struct { + B[int] +} + +type B[T any] struct { +} + +func (b B[T]) Bat() { + t := new(T) + if tt := reflect.TypeOf(t); tt.Kind() != reflect.Pointer || tt.Elem().Kind() != reflect.Int { + panic("unexpected type, want: *int, got: "+tt.String()) + } +} + +type Foo struct { + A[string] +} +func main() { + Foo{}.A.Bat() + Foo{}.A.B.Bat() + Foo{}.Bat() +} -- cgit v1.2.3-54-g00ecf From 6edc57983a39b3cb911ea88b75a7ad39664383ec Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 8 Sep 2021 16:21:33 -0700 Subject: internal/poll: report open fds when TestSplicePipePool fails For #48066 Change-Id: I1152a1c15756df35b71b27d3e7025d97da9e70b0 Reviewed-on: https://go-review.googlesource.com/c/go/+/348579 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Emmanuel Odeke --- src/internal/poll/splice_linux_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/internal/poll/splice_linux_test.go b/src/internal/poll/splice_linux_test.go index 280468c7e7..deac5c3759 100644 --- a/src/internal/poll/splice_linux_test.go +++ b/src/internal/poll/splice_linux_test.go @@ -67,6 +67,13 @@ func TestSplicePipePool(t *testing.T) { } select { case <-expiredTime.C: + t.Logf("descriptors to check: %v", fds) + for _, fd := range fds { + _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0) + if errno == 0 { + t.Errorf("descriptor %d still open", fd) + } + } t.Fatal("at least one pipe is still open") default: } -- cgit v1.2.3-54-g00ecf From 376a079762bf8db387510d50ed718ec1259d1b7c Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 3 Sep 2021 10:49:32 +0700 Subject: cmd/compile: fix unified IR panic when expanding nested inline function When reading body of inlining function, which has another inlined function in the body, the reader still add this inlined function to todoBodies, which it shouldn't because the inlined function was read already. To fix this, introduce new flag to signal that we are done construting all functions in todoBodies, thus the addBody shouldn't add anything to todoBodies then. Updates #48094 Change-Id: I45105dd518f0a7b69c6dcbaf23b957623f271203 Reviewed-on: https://go-review.googlesource.com/c/go/+/347529 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/noder/reader.go | 7 ++++++- src/cmd/compile/internal/noder/unified.go | 1 + test/typeparam/issue48094b.dir/a.go | 8 ++++++++ test/typeparam/issue48094b.dir/b.go | 9 +++++++++ test/typeparam/issue48094b.go | 7 +++++++ 5 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/typeparam/issue48094b.dir/a.go create mode 100644 test/typeparam/issue48094b.dir/b.go create mode 100644 test/typeparam/issue48094b.go diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 57e8476099..48f4368113 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -927,6 +927,11 @@ var bodyReader = map[*ir.Func]pkgReaderIndex{} // constructed. var todoBodies []*ir.Func +// todoBodiesDone signals that we constructed all function in todoBodies. +// This is necessary to prevent reader.addBody adds thing to todoBodies +// when nested inlining happens. +var todoBodiesDone = false + func (r *reader) addBody(fn *ir.Func) { pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict} bodyReader[fn] = pri @@ -937,7 +942,7 @@ func (r *reader) addBody(fn *ir.Func) { return } - if r.curfn == nil { + if r.curfn == nil && !todoBodiesDone { todoBodies = append(todoBodies, fn) return } diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index eff2eeaeff..3d4650a01f 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -136,6 +136,7 @@ func unified(noders []*noder) { } } todoBodies = nil + todoBodiesDone = true // Check that nothing snuck past typechecking. for _, n := range target.Decls { diff --git a/test/typeparam/issue48094b.dir/a.go b/test/typeparam/issue48094b.dir/a.go new file mode 100644 index 0000000000..a113a224f7 --- /dev/null +++ b/test/typeparam/issue48094b.dir/a.go @@ -0,0 +1,8 @@ +// Copyright 2021 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 a + +func F() { G(0) } +func G[T any](t T) {} diff --git a/test/typeparam/issue48094b.dir/b.go b/test/typeparam/issue48094b.dir/b.go new file mode 100644 index 0000000000..242b34aa31 --- /dev/null +++ b/test/typeparam/issue48094b.dir/b.go @@ -0,0 +1,9 @@ +// Copyright 2021 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 b + +import "./a" + +func H() { a.F() } diff --git a/test/typeparam/issue48094b.go b/test/typeparam/issue48094b.go new file mode 100644 index 0000000000..b83fbd7af1 --- /dev/null +++ b/test/typeparam/issue48094b.go @@ -0,0 +1,7 @@ +// compiledir + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From c84f3a4004076b0e3dcef0a17573d85eb90ef5fc Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 8 Sep 2021 10:41:56 +0200 Subject: syscall: drop fallback to pipe in Pipe on linux/arm Follow-up for CL 346789 The minimum required Linux kernel version for Go 1.18 will be changed to 2.6.32, see #45964. The pipe2 syscall was added in 2.6.27, so the fallback to use pipe in Pipe on linux/arm can be removed. For #45964 Change-Id: I8b18244ca1f849f10e90565b4fef80ce777fef69 Reviewed-on: https://go-review.googlesource.com/c/go/+/347349 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/syscall/syscall_linux_arm.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index e887cf788f..fffa4b29b9 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -29,11 +29,7 @@ func Pipe(p []int) (err error) { return EINVAL } var pp [2]_C_int - // Try pipe2 first for Android O, then try pipe for kernel 2.6.23. err = pipe2(&pp, 0) - if err == ENOSYS { - err = pipe(&pp) - } p[0] = int(pp[0]) p[1] = int(pp[1]) return -- cgit v1.2.3-54-g00ecf From b86e8dd0f3a27bc9577690523c9feeb25593bec7 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 9 Sep 2021 10:10:54 +0200 Subject: test/typeparam: fix issue48094b test build CL 347529 broke the longtest builders due to missing -G=3 flag when compiling the added test. Change-Id: I73007801dd6871a8cf3554e957d247f5f56fd641 Reviewed-on: https://go-review.googlesource.com/c/go/+/348330 Trust: Tobias Klauser Trust: Cuong Manh Le Reviewed-by: Cuong Manh Le Run-TryBot: Tobias Klauser TryBot-Result: Go Bot --- test/typeparam/issue48094b.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/typeparam/issue48094b.go b/test/typeparam/issue48094b.go index b83fbd7af1..87b4ff46c1 100644 --- a/test/typeparam/issue48094b.go +++ b/test/typeparam/issue48094b.go @@ -1,4 +1,4 @@ -// compiledir +// compiledir -G=3 // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style -- cgit v1.2.3-54-g00ecf From 9e1eea6f8b0d0266c481df5bc3c808defeaa6f44 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 8 Sep 2021 18:16:36 -0400 Subject: go/types: detect constraint type inference cycles This is a port of CL 347300 to go/types. The test was adjusted to match the differing error positioning in go/types: errors are placed on the ast.CallExpr.Fun, rather than the Lparen. Change-Id: I3dee5d7d75bae18817cc1f04ab0d357d3a6a8198 Reviewed-on: https://go-review.googlesource.com/c/go/+/348689 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/infer.go | 124 +++++++++++++++++++++++++ src/go/types/testdata/fixedbugs/issue48136.go2 | 36 +++++++ 2 files changed, 160 insertions(+) create mode 100644 src/go/types/testdata/fixedbugs/issue48136.go2 diff --git a/src/go/types/infer.go b/src/go/types/infer.go index e6417545e9..7314a614d0 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -8,6 +8,7 @@ package types import ( + "fmt" "go/token" "strings" ) @@ -404,6 +405,34 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type, report bool) (t } } + // The data structure of each (provided or inferred) type represents a graph, where + // each node corresponds to a type and each (directed) vertice points to a component + // type. The substitution process described above repeatedly replaces type parameter + // nodes in these graphs with the graphs of the types the type parameters stand for, + // which creates a new (possibly bigger) graph for each type. + // The substitution process will not stop if the replacement graph for a type parameter + // also contains that type parameter. + // For instance, for [A interface{ *A }], without any type argument provided for A, + // unification produces the type list [*A]. Substituting A in *A with the value for + // A will lead to infinite expansion by producing [**A], [****A], [********A], etc., + // because the graph A -> *A has a cycle through A. + // Generally, cycles may occur across multiple type parameters and inferred types + // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]). + // We eliminate cycles by walking the graphs for all type parameters. If a cycle + // through a type parameter is detected, cycleFinder nils out the respectice type + // which kills the cycle; this also means that the respective type could not be + // inferred. + // + // TODO(gri) If useful, we could report the respective cycle as an error. We don't + // do this now because type inference will fail anyway, and furthermore, + // constraints with cycles of this kind cannot currently be satisfied by + // any user-suplied type. But should that change, reporting an error + // would be wrong. + w := cycleFinder{tparams, types, make(map[Type]bool)} + for _, t := range tparams { + w.typ(t) // t != nil + } + // dirty tracks the indices of all types that may still contain type parameters. // We know that nil type entries and entries corresponding to provided (non-nil) // type arguments are clean, so exclude them from the start. @@ -452,3 +481,98 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type, report bool) (t return } + +type cycleFinder struct { + tparams []*TypeParam + types []Type + seen map[Type]bool +} + +func (w *cycleFinder) typ(typ Type) { + if w.seen[typ] { + // We have seen typ before. If it is one of the type parameters + // in tparams, iterative substitution will lead to infinite expansion. + // Nil out the corresponding type which effectively kills the cycle. + if tpar, _ := typ.(*TypeParam); tpar != nil { + if i := tparamIndex(w.tparams, tpar); i >= 0 { + // cycle through tpar + w.types[i] = nil + } + } + // If we don't have one of our type parameters, the cycle is due + // to an ordinary recursive type and we can just stop walking it. + return + } + w.seen[typ] = true + defer delete(w.seen, typ) + + switch t := typ.(type) { + case *Basic, *top: + // nothing to do + + case *Array: + w.typ(t.elem) + + case *Slice: + w.typ(t.elem) + + case *Struct: + w.varList(t.fields) + + case *Pointer: + w.typ(t.base) + + // case *Tuple: + // This case should not occur because tuples only appear + // in signatures where they are handled explicitly. + + case *Signature: + // There are no "method types" so we should never see a recv. + assert(t.recv == nil) + if t.params != nil { + w.varList(t.params.vars) + } + if t.results != nil { + w.varList(t.results.vars) + } + + case *Union: + for _, t := range t.terms { + w.typ(t.typ) + } + + case *Interface: + for _, m := range t.methods { + w.typ(m.typ) + } + for _, t := range t.embeddeds { + w.typ(t) + } + + case *Map: + w.typ(t.key) + w.typ(t.elem) + + case *Chan: + w.typ(t.elem) + + case *Named: + for _, tpar := range t.TypeArgs().list() { + w.typ(tpar) + } + + case *TypeParam: + if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil { + w.typ(w.types[i]) + } + + default: + panic(fmt.Sprintf("unexpected %T", typ)) + } +} + +func (w *cycleFinder) varList(list []*Var) { + for _, v := range list { + w.typ(v.typ) + } +} diff --git a/src/go/types/testdata/fixedbugs/issue48136.go2 b/src/go/types/testdata/fixedbugs/issue48136.go2 new file mode 100644 index 0000000000..b87f84ae64 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue48136.go2 @@ -0,0 +1,36 @@ +// Copyright 2021 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 p + +func f1[P interface{ *P }]() {} +func f2[P interface{ func(P) }]() {} +func f3[P, Q interface{ func(Q) P }]() {} +func f4[P interface{ *Q }, Q interface{ func(P) }]() {} +func f5[P interface{ func(P) }]() {} +func f6[P interface { *Tree[P] }, Q any ]() {} + +func _() { + f1 /* ERROR cannot infer P */ () + f2 /* ERROR cannot infer P */ () + f3 /* ERROR cannot infer P */ () + f4 /* ERROR cannot infer P */ () + f5 /* ERROR cannot infer P */ () + f6 /* ERROR cannot infer P */ () +} + +type Tree[P any] struct { + left, right *Tree[P] + data P +} + +// test case from issue + +func foo[Src interface { func() Src }]() Src { + return foo[Src] +} + +func _() { + foo /* ERROR cannot infer Src */ () +} -- cgit v1.2.3-54-g00ecf From d2a77f1c76dcc960d8548fa47ec29fcb1b2e5833 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 8 Sep 2021 18:27:31 -0400 Subject: go/types: handle recursive type parameter constraints This is a port of CL 348090 to go/types. Notably, unlike in types2, declareTypeParams was previously setting the default constraint to the empty interface, not nil, because this was missed in CL 335034 (no changes were made to declareTypeParams). This CL fixes this discrepancy. Change-Id: I0fa54a660ba14c6cbefa81a27ab7eb193df3be20 Reviewed-on: https://go-review.googlesource.com/c/go/+/348690 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/decl.go | 19 +++++++++++---- src/go/types/signature.go | 2 +- src/go/types/testdata/fixedbugs/issue45550.go2 | 10 ++++++++ src/go/types/testdata/fixedbugs/issue47796.go2 | 33 ++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/go/types/testdata/fixedbugs/issue45550.go2 create mode 100644 src/go/types/testdata/fixedbugs/issue47796.go2 diff --git a/src/go/types/decl.go b/src/go/types/decl.go index f679c33a94..8ebe7c6f5b 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -615,7 +615,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { if tdecl.TypeParams != nil { check.openScope(tdecl, "type parameters") defer check.closeScope() - named.tparams = check.collectTypeParams(tdecl.TypeParams) + check.collectTypeParams(&named.tparams, tdecl.TypeParams) } // determine underlying type of named @@ -647,7 +647,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { } } -func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList { +func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList) { var tparams []*TypeParam // Declare type parameters up-front, with empty interface as type bound. // The scope of type parameters starts at the beginning of the type parameter @@ -656,6 +656,11 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList { tparams = check.declareTypeParams(tparams, f.Names) } + // Set the type parameters before collecting the type constraints because + // the parameterized type may be used by the constraints (issue #47887). + // Example: type T[P T[P]] interface{} + *dst = bindTParams(tparams) + index := 0 var bound Type for _, f := range list.List { @@ -670,14 +675,18 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) *TypeParamList { next: index += len(f.Names) } - - return bindTParams(tparams) } func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam { + // Use Typ[Invalid] for the type constraint to ensure that a type + // is present even if the actual constraint has not been assigned + // yet. + // TODO(gri) Need to systematically review all uses of type parameter + // constraints to make sure we don't rely on them if they + // are not properly set yet. for _, name := range names { tname := NewTypeName(name.Pos(), check.pkg, name.Name, nil) - tpar := check.newTypeParam(tname, &emptyInterface) // assigns type to tpar as a side-effect + tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tpar as a side-effect check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position tparams = append(tparams, tpar) } diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 88ea07d5d3..ec2030a689 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -152,7 +152,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast } if ftyp.TypeParams != nil { - sig.tparams = check.collectTypeParams(ftyp.TypeParams) + check.collectTypeParams(&sig.tparams, ftyp.TypeParams) // Always type-check method type parameters but complain that they are not allowed. // (A separate check is needed when type-checking interface method signatures because // they don't have a receiver specification.) diff --git a/src/go/types/testdata/fixedbugs/issue45550.go2 b/src/go/types/testdata/fixedbugs/issue45550.go2 new file mode 100644 index 0000000000..c3e9e34b87 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue45550.go2 @@ -0,0 +1,10 @@ +// Copyright 2021 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 p + +type Builder[T interface{ struct{ Builder[T] } }] struct{} +type myBuilder struct { + Builder[myBuilder /* ERROR myBuilder does not satisfy */] +} diff --git a/src/go/types/testdata/fixedbugs/issue47796.go2 b/src/go/types/testdata/fixedbugs/issue47796.go2 new file mode 100644 index 0000000000..9c10683e22 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue47796.go2 @@ -0,0 +1,33 @@ +// Copyright 2021 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 p + +// parameterized types with self-recursive constraints +type ( + T1[P T1[P]] interface{} + T2[P, Q T2[P, Q]] interface{} + T3[P T2[P, Q], Q interface{ ~string }] interface{} + + T4a[P T4a[P]] interface{ ~int } + T4b[P T4b[int]] interface{ ~int } + T4c[P T4c[string /* ERROR string does not satisfy T4c\[string\] */]] interface{ ~int } + + // mutually recursive constraints + T5[P T6[P]] interface{ int } + T6[P T5[P]] interface{ int } +) + +// verify that constraints are checked as expected +var ( + _ T1[int] + _ T2[int, string] + _ T3[int, string] +) + +// test case from issue + +type Eq[a Eq[a]] interface { + Equal(that a) bool +} -- cgit v1.2.3-54-g00ecf From 66f0d35f7145ca8d4e45b04292f44831d6610b3b Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 8 Sep 2021 19:03:37 -0400 Subject: go/types: reduce number of delayed functions This is a port of CL 348018 to go/types. It differs from that CL due to the way that field lists are represented in go/ast. Change-Id: Ib5a0243b44d0bf9e95d039f624c668f8c329f8fa Reviewed-on: https://go-review.googlesource.com/c/go/+/348691 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/decl.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 8ebe7c6f5b..c1506f6dbd 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -663,11 +663,21 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList index := 0 var bound Type + var bounds []Type + var posns []positioner // bound positions for _, f := range list.List { if f.Type == nil { goto next } - bound = check.boundType(f.Type) + // The predeclared identifier "any" is visible only as a type bound in a type parameter list. + // If we allow "any" for general use, this if-statement can be removed (issue #33232). + if name, _ := unparen(f.Type).(*ast.Ident); name != nil && name.Name == "any" && check.lookup("any") == universeAny { + bound = universeAny.Type() + } else { + bound = check.typ(f.Type) + } + bounds = append(bounds, bound) + posns = append(posns, f.Type) for i := range f.Names { tparams[index+i].bound = bound } @@ -675,6 +685,15 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList next: index += len(f.Names) } + + check.later(func() { + for i, bound := range bounds { + u := under(bound) + if _, ok := u.(*Interface); !ok && u != Typ[Invalid] { + check.errorf(posns[i], _Todo, "%s is not an interface", bound) + } + } + }) } func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident) []*TypeParam { @@ -698,25 +717,6 @@ func (check *Checker) declareTypeParams(tparams []*TypeParam, names []*ast.Ident return tparams } -// boundType type-checks the type expression e and returns its type, or Typ[Invalid]. -// The type must be an interface, including the predeclared type "any". -func (check *Checker) boundType(e ast.Expr) Type { - // The predeclared identifier "any" is visible only as a type bound in a type parameter list. - // If we allow "any" for general use, this if-statement can be removed (issue #33232). - if name, _ := unparen(e).(*ast.Ident); name != nil && name.Name == "any" && check.lookup("any") == universeAny { - return universeAny.Type() - } - - bound := check.typ(e) - check.later(func() { - u := under(bound) - if _, ok := u.(*Interface); !ok && u != Typ[Invalid] { - check.errorf(e, _Todo, "%s is not an interface", bound) - } - }) - return bound -} - func (check *Checker) collectMethods(obj *TypeName) { // get associated methods // (Checker.collectObjects only collects methods with non-blank names; -- cgit v1.2.3-54-g00ecf From a295b3cec80b048048bf51a0462764dc337ca6ef Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Wed, 8 Sep 2021 14:03:53 -0700 Subject: test: re-enable AsmCheck tests for types2-based frontends run.go has logic for being able to run tests with various -G flags, but not all test types (in particular, "asmcheck" tests) support configuring non-default -G levels. The intention was that these tests would continue running in the default mode (at the time -G=0), but at some point it seems like we accidentally disabled them all together (if it ever worked correctly in the first place). Fixes #48247. Change-Id: I13917cb0012cbe522d29b23b888de6136872ead4 Reviewed-on: https://go-review.googlesource.com/c/go/+/348671 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Michael Munday --- test/run.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/run.go b/test/run.go index 76621d9242..790b54bfd2 100644 --- a/test/run.go +++ b/test/run.go @@ -780,11 +780,13 @@ func (t *test) run() { } default: - // we don't know how to add -G for this test yet - if *verbose { - fmt.Printf("excl\t%s\n", t.goFileName()) + if t.glevel != CompilerDefaultGLevel { + // we don't know how to add -G for this test yet + if *verbose { + fmt.Printf("excl\t%s\n", t.goFileName()) + } + return false } - return false } return true -- cgit v1.2.3-54-g00ecf From 19457a58e565ff5b480c3806fe02fbc7ccdf32f0 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 9 Sep 2021 08:09:57 -0700 Subject: cmd/compile: stenciled conversions might be NOPs A generic conversion might be required for when converting T->interface{}. When stenciled with T=interface{}, then that conversion doesn't need to do anything. Fixes #48276 Change-Id: Ife65d01c99fbd0895cb7eec79df9e93e752b1fa5 Reviewed-on: https://go-review.googlesource.com/c/go/+/348736 Trust: Keith Randall Run-TryBot: Keith Randall Reviewed-by: Cuong Manh Le TryBot-Result: Go Bot --- src/cmd/compile/internal/noder/stencil.go | 6 ++++++ test/typeparam/issue48276a.go | 19 +++++++++++++++++++ test/typeparam/issue48276a.out | 1 + test/typeparam/issue48276b.go | 15 +++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 test/typeparam/issue48276a.go create mode 100644 test/typeparam/issue48276a.out create mode 100644 test/typeparam/issue48276b.go diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 1c22fc2ac0..a524ddc2a0 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -1177,6 +1177,12 @@ func (subst *subster) node(n ir.Node) ir.Node { case ir.OCONVIFACE: x := x.(*ir.ConvExpr) + if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() { + // Was T->interface{}, after stenciling it is now interface{}->interface{}. + // No longer need the conversion. See issue 48276. + m.(*ir.ConvExpr).SetOp(ir.OCONVNOP) + break + } // Note: x's argument is still typed as a type parameter. // m's argument now has an instantiated type. if x.X.Type().HasTParam() || (x.X.Type().IsInterface() && x.Type().HasTParam()) { diff --git a/test/typeparam/issue48276a.go b/test/typeparam/issue48276a.go new file mode 100644 index 0000000000..060ac3eb7f --- /dev/null +++ b/test/typeparam/issue48276a.go @@ -0,0 +1,19 @@ +// run -gcflags=-G=3 + +// Copyright 2021 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 main + +import "fmt" + +func main() { + IsZero[interface{}]("") +} + +func IsZero[T comparable](val T) bool { + var zero T + fmt.Printf("%v:%v\n", zero, val) + return val != zero +} diff --git a/test/typeparam/issue48276a.out b/test/typeparam/issue48276a.out new file mode 100644 index 0000000000..7e8a8a9a2e --- /dev/null +++ b/test/typeparam/issue48276a.out @@ -0,0 +1 @@ +: diff --git a/test/typeparam/issue48276b.go b/test/typeparam/issue48276b.go new file mode 100644 index 0000000000..67c3e3d9f5 --- /dev/null +++ b/test/typeparam/issue48276b.go @@ -0,0 +1,15 @@ +// run -gcflags=-G=3 + +// Copyright 2021 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 main + +func main() { + f[interface{}](nil) +} + +func f[T any](x T) { + var _ interface{} = x +} -- cgit v1.2.3-54-g00ecf From a53e3d5f885ca7a0df1cd6cf65faa5b63a802dce Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Fri, 6 Aug 2021 10:33:08 -0700 Subject: net: deprecate (net.Error).Temporary Fixes #45729. Change-Id: I819dd0cc79fc23baac46cafd78bb80f5133c992b Reviewed-on: https://go-review.googlesource.com/c/go/+/340261 Trust: Damien Neil Trust: Bryan C. Mills Run-TryBot: Damien Neil TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/net/net.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/net/net.go b/src/net/net.go index a7c65fff79..ab6aeaac2f 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -396,8 +396,12 @@ type Listener interface { // An Error represents a network error. type Error interface { error - Timeout() bool // Is the error a timeout? - Temporary() bool // Is the error temporary? + Timeout() bool // Is the error a timeout? + + // Deprecated: Temporary errors are not well-defined. + // Most "temporary" errors are timeouts, and the few exceptions are surprising. + // Do not use this method. + Temporary() bool } // Various errors contained in OpError. -- cgit v1.2.3-54-g00ecf From ea434450c26431332c4cc376996e2b917fd09873 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Fri, 3 Sep 2021 21:35:59 +0000 Subject: reflect: add hooks for dealing with narrow width floats Currently on amd64 and arm64, float32 values just live in the bottom 32 bits of the register, so reflect simply places them there in a RegArgs for reflectcall to load them. This works fine because both of these platforms don't care what the upper 32 bits are, and have instructions to operate on float32 values specifically that we use. In sum, the representation of the float32 in memory is identical to that of the representation in a register. On other platforms, however, the representation of FP values differ depending on whether they're in memory or in a register. For instance, on ppc64, all floating point values get promoted to a float64 when loaded to a register (i.e. there's only one set of FP instructions). As another example, on riscv64, narrow-width floats in registers need to be NaN-boxed. What all this means is that for supporting the register ABI on these platforms, reflect needs to do a little extra work to ensure that the representation of FP values in a RegArgs matches the representation it takes on in a register. For this purpose, this change abstracts away the action of storing values into a RegArgs a little bit and adds a platform-specific hook which currently does nothing but copy the value. For #40724. Change-Id: I65dcc7d86d5602a584f86026ac204564617f4c5a Reviewed-on: https://go-review.googlesource.com/c/go/+/347566 Trust: Michael Knyszek Run-TryBot: Michael Knyszek TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/internal/abi/abi.go | 28 ++++++++------------------ src/reflect/abi.go | 42 +++++++++++++++++++++++++++++++++++++++ src/reflect/float32reg_generic.go | 21 ++++++++++++++++++++ src/reflect/value.go | 24 +++++++++++----------- 4 files changed, 83 insertions(+), 32 deletions(-) create mode 100644 src/reflect/float32reg_generic.go diff --git a/src/internal/abi/abi.go b/src/internal/abi/abi.go index eadff248d9..46dc593bd7 100644 --- a/src/internal/abi/abi.go +++ b/src/internal/abi/abi.go @@ -19,6 +19,14 @@ import ( // when it may not be safe to keep them only in the integer // register space otherwise. type RegArgs struct { + // Values in these slots should be precisely the bit-by-bit + // representation of how they would appear in a register. + // + // This means that on big endian arches, integer values should + // be in the top bits of the slot. Floats are usually just + // directly represented, but some architectures treat narrow + // width floating point values specially (e.g. they're promoted + // first, or they need to be NaN-boxed). Ints [IntArgRegs]uintptr // untyped integer registers Floats [FloatArgRegs]uint64 // untyped float registers @@ -56,26 +64,6 @@ func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer { return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset) } -// FloatRegArgAddr returns a pointer inside of r.Floats[reg] that is appropriately -// offset for an argument of size argSize. -// -// argSize must be non-zero, fit in a register, and a power-of-two. -// -// This method is a helper for dealing with the endianness of different CPU -// architectures, since sub-word-sized arguments in big endian architectures -// need to be "aligned" to the upper edge of the register to be interpreted -// by the CPU correctly. -func (r *RegArgs) FloatRegArgAddr(reg int, argSize uintptr) unsafe.Pointer { - if argSize > EffectiveFloatRegSize || argSize == 0 || argSize&(argSize-1) != 0 { - panic("invalid argSize") - } - offset := uintptr(0) - if goarch.BigEndian { - offset = EffectiveFloatRegSize - argSize - } - return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Floats[reg])) + offset) -} - // IntArgRegBitmap is a bitmap large enough to hold one bit per // integer argument/return register. type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8 diff --git a/src/reflect/abi.go b/src/reflect/abi.go index 9ddde3ae57..2ce7ca2615 100644 --- a/src/reflect/abi.go +++ b/src/reflect/abi.go @@ -467,3 +467,45 @@ func newAbiDesc(t *funcType, rcvr *rtype) abiDesc { out.stackBytes -= retOffset return abiDesc{in, out, stackCallArgsSize, retOffset, spill, stackPtrs, inRegPtrs, outRegPtrs} } + +// intFromReg loads an argSize sized integer from reg and places it at to. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +func intFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) { + memmove(to, r.IntRegArgAddr(reg, argSize), argSize) +} + +// intToReg loads an argSize sized integer and stores it into reg. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +func intToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) { + memmove(r.IntRegArgAddr(reg, argSize), from, argSize) +} + +// floatFromReg loads a float value from its register representation in r. +// +// argSize must be 4 or 8. +func floatFromReg(r *abi.RegArgs, reg int, argSize uintptr, to unsafe.Pointer) { + switch argSize { + case 4: + *(*float32)(to) = archFloat32FromReg(r.Floats[reg]) + case 8: + *(*float64)(to) = *(*float64)(unsafe.Pointer(&r.Floats[reg])) + default: + panic("bad argSize") + } +} + +// floatToReg stores a float value in its register representation in r. +// +// argSize must be either 4 or 8. +func floatToReg(r *abi.RegArgs, reg int, argSize uintptr, from unsafe.Pointer) { + switch argSize { + case 4: + r.Floats[reg] = archFloat32ToReg(*(*float32)(from)) + case 8: + r.Floats[reg] = *(*uint64)(from) + default: + panic("bad argSize") + } +} diff --git a/src/reflect/float32reg_generic.go b/src/reflect/float32reg_generic.go new file mode 100644 index 0000000000..381d458057 --- /dev/null +++ b/src/reflect/float32reg_generic.go @@ -0,0 +1,21 @@ +// Copyright 2021 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 reflect + +import "unsafe" + +// This file implements a straightforward conversion of a float32 +// value into its representation in a register. This conversion +// applies for amd64 and arm64. It is also chosen for the case of +// zero argument registers, but is not used. + +func archFloat32FromReg(reg uint64) float32 { + i := uint32(reg) + return *(*float32)(unsafe.Pointer(&i)) +} + +func archFloat32ToReg(val float32) uint64 { + return uint64(*(*uint32)(unsafe.Pointer(&val))) +} diff --git a/src/reflect/value.go b/src/reflect/value.go index 3c2172135e..bf29d1bb3a 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -508,7 +508,7 @@ func (v Value) call(op string, in []Value) []Value { // Copy values to "integer registers." if v.flag&flagIndir != 0 { offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(regArgs.IntRegArgAddr(st.ireg, st.size), offset, st.size) + intToReg(®Args, st.ireg, st.size, offset) } else { if st.kind == abiStepPointer { // Duplicate this pointer in the pointer area of the @@ -524,7 +524,7 @@ func (v Value) call(op string, in []Value) []Value { panic("attempted to copy pointer to FP register") } offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(regArgs.FloatRegArgAddr(st.freg, st.size), offset, st.size) + floatToReg(®Args, st.freg, st.size, offset) default: panic("unknown ABI part kind") } @@ -610,13 +610,13 @@ func (v Value) call(op string, in []Value) []Value { switch st.kind { case abiStepIntReg: offset := add(s, st.offset, "precomputed value offset") - memmove(offset, regArgs.IntRegArgAddr(st.ireg, st.size), st.size) + intFromReg(®Args, st.ireg, st.size, offset) case abiStepPointer: s := add(s, st.offset, "precomputed value offset") *((*unsafe.Pointer)(s)) = regArgs.Ptrs[st.ireg] case abiStepFloatReg: offset := add(s, st.offset, "precomputed value offset") - memmove(offset, regArgs.FloatRegArgAddr(st.freg, st.size), st.size) + floatFromReg(®Args, st.freg, st.size, offset) case abiStepStack: panic("register-based return value has stack component") default: @@ -698,13 +698,13 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs switch st.kind { case abiStepIntReg: offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(offset, regs.IntRegArgAddr(st.ireg, st.size), st.size) + intFromReg(regs, st.ireg, st.size, offset) case abiStepPointer: s := add(v.ptr, st.offset, "precomputed value offset") *((*unsafe.Pointer)(s)) = regs.Ptrs[st.ireg] case abiStepFloatReg: offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(offset, regs.FloatRegArgAddr(st.freg, st.size), st.size) + floatFromReg(regs, st.freg, st.size, offset) case abiStepStack: panic("register-based return value has stack component") default: @@ -784,7 +784,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs // Copy values to "integer registers." if v.flag&flagIndir != 0 { offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(regs.IntRegArgAddr(st.ireg, st.size), offset, st.size) + intToReg(regs, st.ireg, st.size, offset) } else { // Only populate the Ints space on the return path. // This is safe because out is kept alive until the @@ -799,7 +799,7 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs panic("attempted to copy pointer to FP register") } offset := add(v.ptr, st.offset, "precomputed value offset") - memmove(regs.FloatRegArgAddr(st.freg, st.size), offset, st.size) + floatToReg(regs, st.freg, st.size, offset) default: panic("unknown ABI part kind") } @@ -982,9 +982,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a methodRegs.Ptrs[mStep.ireg] = *(*unsafe.Pointer)(from) fallthrough // We need to make sure this ends up in Ints, too. case abiStepIntReg: - memmove(methodRegs.IntRegArgAddr(mStep.ireg, mStep.size), from, mStep.size) + intToReg(&methodRegs, mStep.ireg, mStep.size, from) case abiStepFloatReg: - memmove(methodRegs.FloatRegArgAddr(mStep.freg, mStep.size), from, mStep.size) + floatToReg(&methodRegs, mStep.freg, mStep.size, from) default: panic("unexpected method step") } @@ -1000,9 +1000,9 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *a // Do the pointer copy directly so we get a write barrier. *(*unsafe.Pointer)(to) = valueRegs.Ptrs[vStep.ireg] case abiStepIntReg: - memmove(to, valueRegs.IntRegArgAddr(vStep.ireg, vStep.size), vStep.size) + intFromReg(valueRegs, vStep.ireg, vStep.size, to) case abiStepFloatReg: - memmove(to, valueRegs.FloatRegArgAddr(vStep.freg, vStep.size), vStep.size) + floatFromReg(valueRegs, vStep.freg, vStep.size, to) default: panic("unexpected value step") } -- cgit v1.2.3-54-g00ecf From f9271e4f853eacded38fd6d626948e035cfd608c Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 9 Sep 2021 09:26:40 -0400 Subject: go/types, types2: rename RParams -> RecvTypeParams To be consistent with CL 348376, spell out 'RecvTypeParams' in go/types and types2 API. Updates #47916 Change-Id: If8b3fd4274ccb944bd0ff04d7007e94e5fba61c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/348810 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/importer/iimport.go | 2 +- src/cmd/compile/internal/noder/reader2.go | 2 +- src/cmd/compile/internal/noder/types.go | 2 +- src/cmd/compile/internal/noder/writer.go | 6 +++--- src/cmd/compile/internal/types2/call.go | 6 +++--- src/cmd/compile/internal/types2/lookup.go | 8 ++++---- src/cmd/compile/internal/types2/signature.go | 18 +++++++++--------- src/go/internal/gcimporter/iimport.go | 2 +- src/go/types/call.go | 6 +++--- src/go/types/lookup.go | 6 +++--- src/go/types/signature.go | 18 +++++++++--------- 11 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 8fdd879705..b61b1e97fb 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -349,7 +349,7 @@ func (r *importReader) obj(name string) { for i := range rparams { rparams[i] = types2.AsTypeParam(targs.At(i)) } - msig.SetRParams(rparams) + msig.SetRecvTypeParams(rparams) } named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig)) diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go index 0cfde24b58..dcd9a65f40 100644 --- a/src/cmd/compile/internal/noder/reader2.go +++ b/src/cmd/compile/internal/noder/reader2.go @@ -492,7 +492,7 @@ func (r *reader2) method() *types2.Func { rparams := r.typeParamNames() sig := r.signature(r.param()) - sig.SetRParams(rparams) + sig.SetRecvTypeParams(rparams) _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go. return types2.NewFunc(pos, pkg, name, sig) diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go index b0b9c1592a..03fb96c48b 100644 --- a/src/cmd/compile/internal/noder/types.go +++ b/src/cmd/compile/internal/noder/types.go @@ -309,7 +309,7 @@ func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { meth2 = newsym.Def.(*ir.Name) } else { meth2 = ir.NewNameAt(meth.Pos(), newsym) - rparams := types2.AsSignature(m.Type()).RParams() + rparams := types2.AsSignature(m.Type()).RecvTypeParams() tparams := make([]*types.Type, rparams.Len()) for i := range tparams { tparams[i] = g.typ1(rparams.At(i)) diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index e1413da1d8..6a66bea239 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -648,7 +648,7 @@ func (w *writer) method(wext *writer, meth *types2.Func) { w.sync(syncMethod) w.pos(meth) w.selector(meth) - w.typeParamNames(sig.RParams()) + w.typeParamNames(sig.RecvTypeParams()) w.param(sig.Recv()) w.signature(sig) @@ -1665,7 +1665,7 @@ func (w *writer) pkgDecl(decl syntax.Decl) { obj := w.p.info.Defs[decl.Name].(*types2.Func) sig := obj.Type().(*types2.Signature) - if sig.RParams() != nil || sig.TypeParams() != nil { + if sig.RecvTypeParams() != nil || sig.TypeParams() != nil { break // skip generic functions } @@ -1851,7 +1851,7 @@ func objTypeParams(obj types2.Object) *types2.TypeParamList { case *types2.Func: sig := obj.Type().(*types2.Signature) if sig.Recv() != nil { - return sig.RParams() + return sig.RecvTypeParams() } return sig.TypeParams() case *types2.TypeName: diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index f6aaa461b9..ba3bb475a3 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -535,7 +535,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // the signature accordingly. // TODO(gri) factor this code out sig := m.typ.(*Signature) - if sig.RParams().Len() > 0 { + if sig.RecvTypeParams().Len() > 0 { // For inference to work, we must use the receiver type // matching the receiver in the actual method declaration. // If the method is embedded, the matching receiver is the @@ -564,7 +564,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // the receiver type arguments here, the receiver must be be otherwise invalid // and an error has been reported elsewhere. arg := operand{mode: variable, expr: x.expr, typ: recv} - targs := check.infer(m.pos, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) + targs := check.infer(m.pos, sig.RecvTypeParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) //check.dump("### inferred targs = %s", targs) if targs == nil { // We may reach here if there were other errors (see issue #40056). @@ -574,7 +574,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // (If we modify m, some tests will fail; possibly because the m is in use.) // TODO(gri) investigate and provide a correct explanation here copy := *m - copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RParams().list(), targs), nil) + copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RecvTypeParams().list(), targs), nil) obj = © } // TODO(gri) we also need to do substitution for parameterized interface methods diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 67cdc1e68a..81bac7b6ff 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -394,10 +394,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // here. Exit early in this case to prevent an assertion // failure in makeSubstMap. // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.RParams().list()) != Vn.targs.Len() { + if len(ftyp.RecvTypeParams().list()) != Vn.targs.Len() { return } - ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature) + ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.RecvTypeParams().list(), Vn.targs.list()), nil).(*Signature) } // If the methods have type parameters we don't care whether they @@ -416,9 +416,9 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // unimplemented call so that we test this code if we // enable method type parameters. unimplemented() - u.x.init(append(ftyp.RParams().list(), ftyp.TypeParams().list()...)) + u.x.init(append(ftyp.RecvTypeParams().list(), ftyp.TypeParams().list()...)) } else { - u.x.init(ftyp.RParams().list()) + u.x.init(ftyp.RecvTypeParams().list()) } if !u.unify(ftyp, mtyp) { return m, f diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index eeaf1acbd6..009ac77012 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -59,11 +59,11 @@ func (s *Signature) TypeParams() *TypeParamList { return s.tparams } // SetTypeParams sets the type parameters of signature s. func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } -// RParams returns the receiver type parameters of signature s, or nil. -func (s *Signature) RParams() *TypeParamList { return s.rparams } +// RecvTypeParams returns the receiver type parameters of signature s, or nil. +func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams } -// SetRParams sets the receiver type params of signature s. -func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } +// SetRecvTypeParams sets the receiver type params of signature s. +func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } // Params returns the parameters of signature s, or nil. func (s *Signature) Params() *Tuple { return s.params } @@ -138,14 +138,14 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] } // provide type parameter bounds // - only do this if we have the right number (otherwise an error is reported elsewhere) - if sig.RParams().Len() == len(recvTParams) { + if sig.RecvTypeParams().Len() == len(recvTParams) { // We have a list of *TypeNames but we need a list of Types. - list := make([]Type, sig.RParams().Len()) - for i, t := range sig.RParams().list() { + list := make([]Type, sig.RecvTypeParams().Len()) + for i, t := range sig.RecvTypeParams().list() { list[i] = t } smap := makeSubstMap(recvTParams, list) - for i, tpar := range sig.RParams().list() { + for i, tpar := range sig.RecvTypeParams().list() { bound := recvTParams[i].bound // bound is (possibly) parameterized in the context of the // receiver type declaration. Substitute parameters for the @@ -213,7 +213,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] T.expand(nil) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). - if T.TypeArgs() != nil && sig.RParams() == nil { + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ) break } diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index 1fe139da17..039fc6a61b 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -339,7 +339,7 @@ func (r *importReader) obj(name string) { for i := range rparams { rparams[i], _ = targs.At(i).(*types.TypeParam) } - msig.SetRParams(rparams) + msig.SetRecvTypeParams(rparams) } named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig)) diff --git a/src/go/types/call.go b/src/go/types/call.go index 3710756c29..4de5fed46e 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -537,7 +537,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // the signature accordingly. // TODO(gri) factor this code out sig := m.typ.(*Signature) - if sig.RParams().Len() > 0 { + if sig.RecvTypeParams().Len() > 0 { // For inference to work, we must use the receiver type // matching the receiver in the actual method declaration. // If the method is embedded, the matching receiver is the @@ -565,7 +565,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // the receiver type arguments here, the receiver must be be otherwise invalid // and an error has been reported elsewhere. arg := operand{mode: variable, expr: x.expr, typ: recv} - targs := check.infer(m, sig.RParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) + targs := check.infer(m, sig.RecvTypeParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) if targs == nil { // We may reach here if there were other errors (see issue #40056). goto Error @@ -574,7 +574,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // (If we modify m, some tests will fail; possibly because the m is in use.) // TODO(gri) investigate and provide a correct explanation here copy := *m - copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RParams().list(), targs), nil) + copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RecvTypeParams().list(), targs), nil) obj = © } // TODO(gri) we also need to do substitution for parameterized interface methods diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index f5bdd31a6f..4664a0b33b 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -392,10 +392,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // here. Exit early in this case to prevent an assertion // failure in makeSubstMap. // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.RParams().list()) != Vn.targs.Len() { + if len(ftyp.RecvTypeParams().list()) != Vn.targs.Len() { return } - ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.RParams().list(), Vn.targs.list()), nil).(*Signature) + ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.RecvTypeParams().list(), Vn.targs.list()), nil).(*Signature) } // If the methods have type parameters we don't care whether they @@ -404,7 +404,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // TODO(gri) is this always correct? what about type bounds? // (Alternative is to rename/subst type parameters and compare.) u := newUnifier(true) - u.x.init(ftyp.RParams().list()) + u.x.init(ftyp.RecvTypeParams().list()) if !u.unify(ftyp, mtyp) { return m, f } diff --git a/src/go/types/signature.go b/src/go/types/signature.go index ec2030a689..37811828ee 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -61,11 +61,11 @@ func (s *Signature) TypeParams() *TypeParamList { return s.tparams } // SetTypeParams sets the type parameters of signature s. func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) } -// RParams returns the receiver type parameters of signature s, or nil. -func (s *Signature) RParams() *TypeParamList { return s.rparams } +// RecvTypeParams returns the receiver type parameters of signature s, or nil. +func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams } -// SetRParams sets the receiver type params of signature s. -func (s *Signature) SetRParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } +// SetRecvTypeParams sets the receiver type params of signature s. +func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) } // Params returns the parameters of signature s, or nil. func (s *Signature) Params() *Tuple { return s.params } @@ -133,14 +133,14 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast } // provide type parameter bounds // - only do this if we have the right number (otherwise an error is reported elsewhere) - if sig.RParams().Len() == len(recvTParams) { + if sig.RecvTypeParams().Len() == len(recvTParams) { // We have a list of *TypeNames but we need a list of Types. - list := make([]Type, sig.RParams().Len()) - for i, t := range sig.RParams().list() { + list := make([]Type, sig.RecvTypeParams().Len()) + for i, t := range sig.RecvTypeParams().list() { list[i] = t } smap := makeSubstMap(recvTParams, list) - for i, tpar := range sig.RParams().list() { + for i, tpar := range sig.RecvTypeParams().list() { bound := recvTParams[i].bound // bound is (possibly) parameterized in the context of the // receiver type declaration. Substitute parameters for the @@ -203,7 +203,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast T.expand(nil) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). - if T.TypeArgs() != nil && sig.RParams() == nil { + if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { check.errorf(atPos(recv.pos), _Todo, "cannot define methods on instantiated type %s", recv.typ) break } -- cgit v1.2.3-54-g00ecf From b9e1a24581b6282ee930a50bbe498d24ef77f486 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Wed, 8 Sep 2021 08:41:54 -0700 Subject: cmd/compile: fix case where init info of OAS node is dropped When an OAS node is converted to an OSELRECV2 node in tcSelect(), the possible DCL node in the Init field was being dropped, since a completely new node was being created and the Init field was not set. I don't expect n.Init() to be set for the ORECV case, but the code now deals with that too. Fixed bug in both tcSelect() and transformSelect(). Fixes #48289 Change-Id: I09918a70f7cbaa4aa9a17546169f908a8787df15 Reviewed-on: https://go-review.googlesource.com/c/go/+/348569 Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Reviewed-by: Matthew Dempsky Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/noder/transform.go | 1 + src/cmd/compile/internal/typecheck/stmt.go | 9 +++++---- test/fixedbugs/issue48289.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 test/fixedbugs/issue48289.go diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index b278f3db09..8173bfc747 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -554,6 +554,7 @@ func transformSelect(sel *ir.SelectStmt) { } selrecv.Def = def selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) ncase.Comm = selrecv } switch n.Op() { diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go index c322d490e5..9a02c1752c 100644 --- a/src/cmd/compile/internal/typecheck/stmt.go +++ b/src/cmd/compile/internal/typecheck/stmt.go @@ -395,10 +395,11 @@ func tcSelect(sel *ir.SelectStmt) { n := Stmt(ncase.Comm) ncase.Comm = n oselrecv2 := func(dst, recv ir.Node, def bool) { - n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) - n.Def = def - n.SetTypecheck(1) - ncase.Comm = n + selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv}) + selrecv.Def = def + selrecv.SetTypecheck(1) + selrecv.SetInit(n.Init()) + ncase.Comm = selrecv } switch n.Op() { default: diff --git a/test/fixedbugs/issue48289.go b/test/fixedbugs/issue48289.go new file mode 100644 index 0000000000..94dbeee34c --- /dev/null +++ b/test/fixedbugs/issue48289.go @@ -0,0 +1,28 @@ +// run + +// Copyright 2021 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 main + +import "fmt" + +func main() { + ch := make(chan int, 1) + + var ptrs [2]*int + for i := range ptrs { + ch <- i + select { + case x := <-ch: + ptrs[i] = &x + } + } + + for i, ptr := range ptrs { + if *ptr != i { + panic(fmt.Sprintf("got *ptr %d, want %d", *ptr, i)) + } + } +} -- cgit v1.2.3-54-g00ecf From fb84e99eb76615f63fc2d6bc93c244a84d6e9c22 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 9 Sep 2021 11:17:39 -0700 Subject: test: add compiler regress tests for #46461 gri@ reports that types2 now correctly handles when type parameters recursively refer back to the parameterized type, so we might as well add tests to exercise that. Unified IR also correctly handles importing and exporting these types, but -G=3 currently does not. Updates #46461. Change-Id: I272102aa08c40c980b9aeeca9f834291dfbbcc3e Reviewed-on: https://go-review.googlesource.com/c/go/+/348738 Trust: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/internal/gcimporter/gcimporter_test.go | 2 ++ test/run.go | 2 ++ test/typeparam/issue46461.go | 13 +++++++++++++ test/typeparam/issue46461b.dir/a.go | 7 +++++++ test/typeparam/issue46461b.dir/b.go | 11 +++++++++++ test/typeparam/issue46461b.go | 7 +++++++ 6 files changed, 42 insertions(+) create mode 100644 test/typeparam/issue46461.go create mode 100644 test/typeparam/issue46461b.dir/a.go create mode 100644 test/typeparam/issue46461b.dir/b.go create mode 100644 test/typeparam/issue46461b.go diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 9f4345d8f9..478534daf2 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -167,6 +167,8 @@ func TestImportTypeparamTests(t *testing.T) { skip := map[string]string{ "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this. "nested.go": "fails to compile", // TODO(rfindley): investigate this. + + "issue46461.go": "known issue with type parameter constraints referring back to parameterized type", } for _, entry := range list { diff --git a/test/run.go b/test/run.go index 790b54bfd2..d2b7b88768 100644 --- a/test/run.go +++ b/test/run.go @@ -2188,6 +2188,8 @@ var g3Failures = setOf( "typeparam/nested.go", // -G=3 doesn't support function-local types with generics + "typeparam/issue46461b.go", // -G=3 fails when type parameters refer back to the parameterized type itself + "typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops ) diff --git a/test/typeparam/issue46461.go b/test/typeparam/issue46461.go new file mode 100644 index 0000000000..2c54a6ba28 --- /dev/null +++ b/test/typeparam/issue46461.go @@ -0,0 +1,13 @@ +// compile -G=3 + +// Copyright 2021 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 p + +type T[U interface{ M() T[U] }] int + +type X int + +func (X) M() T[X] { return 0 } diff --git a/test/typeparam/issue46461b.dir/a.go b/test/typeparam/issue46461b.dir/a.go new file mode 100644 index 0000000000..0d53b3e204 --- /dev/null +++ b/test/typeparam/issue46461b.dir/a.go @@ -0,0 +1,7 @@ +// Copyright 2021 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 a + +type T[U interface{ M() T[U] }] int diff --git a/test/typeparam/issue46461b.dir/b.go b/test/typeparam/issue46461b.dir/b.go new file mode 100644 index 0000000000..3393a375c2 --- /dev/null +++ b/test/typeparam/issue46461b.dir/b.go @@ -0,0 +1,11 @@ +// Copyright 2021 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 b + +import "./a" + +type X int + +func (X) M() a.T[X] { return 0 } diff --git a/test/typeparam/issue46461b.go b/test/typeparam/issue46461b.go new file mode 100644 index 0000000000..87b4ff46c1 --- /dev/null +++ b/test/typeparam/issue46461b.go @@ -0,0 +1,7 @@ +// compiledir -G=3 + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From 2c4f389c0298a37f1f3c000ad8b87e65d46c757f Mon Sep 17 00:00:00 2001 From: "Paul E. Murphy" Date: Tue, 23 Mar 2021 16:17:00 -0500 Subject: cmd/link: enable internal linker in more cases for ppc64le The internal linker is capable of linking the ppc64le linux race detector and approved cgo packages. Likewise, ppc64/linux and ppc64/aix do not support the race detector. Thus, extra code to enforce external linking when using the race detector on ppc64/ppc64le can be removed entirely. Fixes #21961 Change-Id: I10db14f65ee616ee3291e17409e8333e3af7d4df Reviewed-on: https://go-review.googlesource.com/c/go/+/304459 Run-TryBot: Paul Murphy TryBot-Result: Go Bot Trust: Lynn Boger Reviewed-by: Cherry Mui --- src/cmd/dist/test.go | 5 ----- src/cmd/link/internal/ld/config.go | 12 ++++-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index a104b5c8f3..dd4e96ec21 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -984,11 +984,6 @@ func (t *tester) internalLink() bool { // linkmode=internal fails on dragonfly since errno is a TLS relocation. return false } - if gohostarch == "ppc64le" { - // linkmode=internal fails on ppc64le because cmd/link doesn't - // handle the TOC correctly (issue 15409). - return false - } if goos == "android" { return false } diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go index 20f1d0b8c1..4045c97dd7 100644 --- a/src/cmd/link/internal/ld/config.go +++ b/src/cmd/link/internal/ld/config.go @@ -195,8 +195,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { // Internally linking cgo is incomplete on some architectures. // https://golang.org/issue/14449 - // https://golang.org/issue/21961 - if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) { + if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.RISCV64) { return true, buildcfg.GOARCH + " does not support internal cgo" } if iscgo && (buildcfg.GOOS == "android" || buildcfg.GOOS == "dragonfly") { @@ -209,12 +208,9 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) { // windows/arm64 internal linking is not implemented. return true, buildcfg.GOOS + "/" + buildcfg.GOARCH + " does not support internal cgo" } - - // When the race flag is set, the LLVM tsan relocatable file is linked - // into the final binary, which means external linking is required because - // internal linking does not support it. - if *flagRace && ctxt.Arch.InFamily(sys.PPC64) { - return true, "race on " + buildcfg.GOARCH + if iscgo && ctxt.Arch == sys.ArchPPC64 { + // Big Endian PPC64 cgo internal linking is not implemented for aix or linux. + return true, buildcfg.GOOS + " does not support internal cgo" } // Some build modes require work the internal linker cannot do (yet). -- cgit v1.2.3-54-g00ecf From c981874a5a87605b446b3a56abba9907d17e8493 Mon Sep 17 00:00:00 2001 From: korzhao Date: Thu, 9 Sep 2021 21:51:43 +0800 Subject: cmd/compile: fix implement for closure in a global assignment If closure in a global assignment and has a method receiver. We should assign receiver as a global variable, not a local variable. Fixes #48225 Change-Id: I8f65dd6e8baf66a5eff24028d28ad0a594091add Reviewed-on: https://go-review.googlesource.com/c/go/+/348512 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Dan Scales Reviewed-by: Keith Randall Trust: Dan Scales --- src/cmd/compile/internal/noder/stencil.go | 12 +++++++--- test/typeparam/issue48225.go | 37 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 test/typeparam/issue48225.go diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index a524ddc2a0..5069db9fe1 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -396,13 +396,19 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { if rcvrValue != nil { rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum)) g.dnum++ - rcvrVar.Class = ir.PAUTO typed(rcvrValue.Type(), rcvrVar) - rcvrVar.Curfn = outer rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue) rcvrAssign.SetTypecheck(1) rcvrVar.Defn = rcvrAssign - outer.Dcl = append(outer.Dcl, rcvrVar) + if outer == nil { + rcvrVar.Class = ir.PEXTERN + g.target.Decls = append(g.target.Decls, rcvrAssign) + g.target.Externs = append(g.target.Externs, rcvrVar) + } else { + rcvrVar.Class = ir.PAUTO + rcvrVar.Curfn = outer + outer.Dcl = append(outer.Dcl, rcvrVar) + } } // Build body of closure. This involves just calling the wrapped function directly diff --git a/test/typeparam/issue48225.go b/test/typeparam/issue48225.go new file mode 100644 index 0000000000..887ffd8a84 --- /dev/null +++ b/test/typeparam/issue48225.go @@ -0,0 +1,37 @@ +// run -gcflags="-G=3" + +// Copyright 2021 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 main + +import "reflect" + +type Foo[T any] struct { + val int +} + +func (foo Foo[T]) Get() *T { + if foo.val != 1 { + panic("bad val field in Foo receiver") + } + return new(T) +} + +var ( + newInt = Foo[int]{val: 1}.Get + newString = Foo[string]{val: 1}.Get +) + +func main() { + i := newInt() + s := newString() + + if t := reflect.TypeOf(i).String(); t != "*int" { + panic(t) + } + if t := reflect.TypeOf(s).String(); t != "*string" { + panic(t) + } +} -- cgit v1.2.3-54-g00ecf From e1c3f2158fe3129fb44cc92423cfa41e7b6d472c Mon Sep 17 00:00:00 2001 From: korzhao Date: Sun, 29 Aug 2021 05:21:17 +0800 Subject: time: propagate "," separator for fractional seconds into Format In CL 300996 that fixed issue #6189, we made Parse recognize "," as a separator for fractional seconds. However, we didn't modify Format to propagate the separator verbatim from Parse. Without this change, we break prior functionality that relied on a comma being used in Format. Fixes #48037 Change-Id: I6565a25e8657ca3747a58b25acba58f27cdcddc0 Reviewed-on: https://go-review.googlesource.com/c/go/+/345438 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Reviewed-by: Emmanuel Odeke Trust: Cherry Mui --- src/time/format.go | 50 ++++++++++++++++++++++++++++++++++++++----------- src/time/format_test.go | 20 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/time/format.go b/src/time/format.go index 7ae89c557d..c2bffb8ff6 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -146,10 +146,11 @@ const ( stdFracSecond0 // ".0", ".00", ... , trailing zeros included stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted - stdNeedDate = 1 << 8 // need month, day, year - stdNeedClock = 2 << 8 // need hour, minute, second - stdArgShift = 16 // extra argument in high bits, above low stdArgShift - stdMask = 1<> stdArgShift) & 0xfff +} + +func separator(std int) byte { + if (std >> stdSeparatorShift) == 0 { + return '.' + } + return ',' +} + // formatNano appends a fractional second, as nanoseconds, to b // and returns the result. -func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { +func formatNano(b []byte, nanosec uint, std int) []byte { + var ( + n = digitsLen(std) + separator = separator(std) + trim = std&stdMask == stdFracSecond9 + ) u := nanosec var buf [9]byte for start := len(buf); start > 0; { @@ -452,7 +480,7 @@ func formatNano(b []byte, nanosec uint, n int, trim bool) []byte { return b } } - b = append(b, '.') + b = append(b, separator) return append(b, buf[:n]...) } @@ -733,7 +761,7 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { b = appendInt(b, zone/60, 2) b = appendInt(b, zone%60, 2) case stdFracSecond0, stdFracSecond9: - b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9) + b = formatNano(b, uint(t.Nanosecond()), std) } } return b @@ -1165,7 +1193,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) case stdFracSecond0: // stdFracSecond0 requires the exact number of digits as specified in // the layout. - ndigit := 1 + (std >> stdArgShift) + ndigit := 1 + digitsLen(std) if len(value) < ndigit { err = errBad break diff --git a/src/time/format_test.go b/src/time/format_test.go index 1af41e2dfb..93cbcf9401 100644 --- a/src/time/format_test.go +++ b/src/time/format_test.go @@ -832,3 +832,23 @@ func TestQuote(t *testing.T) { } } + +// Issue 48037 +func TestFormatFractionalSecondSeparators(t *testing.T) { + tests := []struct { + s, want string + }{ + {`15:04:05.000`, `21:00:57.012`}, + {`15:04:05.999`, `21:00:57.012`}, + {`15:04:05,000`, `21:00:57,012`}, + {`15:04:05,999`, `21:00:57,012`}, + } + + // The numeric time represents Thu Feb 4 21:00:57.012345600 PST 2009 + time := Unix(0, 1233810057012345600) + for _, tt := range tests { + if q := time.Format(tt.s); q != tt.want { + t.Errorf("Format(%q) = got %q, want %q", tt.s, q, tt.want) + } + } +} -- cgit v1.2.3-54-g00ecf From 73483df406af39e6c244fd2fb90b41c4cfecd51e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 21:30:01 -0700 Subject: cmd/compile/internal/syntax: better error message for missing type constraint For #43527. Change-Id: I8c706e68572286d5675383eb2dfd75b5618b646b Reviewed-on: https://go-review.googlesource.com/c/go/+/348730 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/syntax/parser.go | 22 +++++++++++++++------ .../internal/syntax/testdata/issue43527.go2 | 23 ++++++++++++++++++++++ .../compile/internal/syntax/testdata/tparams.go2 | 8 ++++---- .../types2/testdata/fixedbugs/issue47996.go2 | 2 +- 4 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 src/cmd/compile/internal/syntax/testdata/issue43527.go2 diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index e89796cb31..c836a21c2f 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -1908,8 +1908,9 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* defer p.trace("paramList")() } - var named int // number of parameters that have an explicit name and type/bound - p.list(_Comma, close, func() bool { + var named int // number of parameters that have an explicit name and type + var typed int // number of parameters that have an explicit type + end := p.list(_Comma, close, func() bool { par := p.paramDeclOrNil(name) name = nil // 1st name was consumed if present if par != nil { @@ -1919,6 +1920,9 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* if par.Name != nil && par.Type != nil { named++ } + if par.Type != nil { + typed++ + } list = append(list, par) } return false @@ -1939,10 +1943,11 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* } } else if named != len(list) { // some named => all must have names and types - var pos Pos // left-most error position (or unknown) - var typ Expr + var pos Pos // left-most error position (or unknown) + var typ Expr // current type (from right to left) for i := len(list) - 1; i >= 0; i-- { - if par := list[i]; par.Type != nil { + par := list[i] + if par.Type != nil { typ = par.Type if par.Name == nil { pos = typ.Pos() @@ -1961,7 +1966,12 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* if pos.IsKnown() { var msg string if requireNames { - msg = "type parameters must be named" + if named == typed { + pos = end // position error at closing ] + msg = "missing type constraint" + } else { + msg = "type parameters must be named" + } } else { msg = "mixed named and unnamed parameters" } diff --git a/src/cmd/compile/internal/syntax/testdata/issue43527.go2 b/src/cmd/compile/internal/syntax/testdata/issue43527.go2 new file mode 100644 index 0000000000..dd2c9b1272 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue43527.go2 @@ -0,0 +1,23 @@ +// Copyright 2021 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 p + +type ( + // 0 and 1-element []-lists are syntactically valid + _[A, B /* ERROR missing type constraint */ ] int + _[A, /* ERROR type parameters must be named */ interface{}] int + _[A, B, C /* ERROR missing type constraint */ ] int + _[A B, C /* ERROR missing type constraint */ ] int + _[A B, /* ERROR type parameters must be named */ interface{}] int + _[A B, /* ERROR type parameters must be named */ interface{}, C D] int + _[A B, /* ERROR type parameters must be named */ interface{}, C, D] int + _[A B, /* ERROR type parameters must be named */ interface{}, C, interface{}] int + _[A B, C interface{}, D, /* ERROR type parameters must be named */ interface{}] int +) + +// function type parameters use the same parsing routine - just have a couple of tests + +func _[A, B /* ERROR missing type constraint */ ]() {} +func _[A, /* ERROR type parameters must be named */ interface{}]() {} diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go2 index 42031c3277..8e47ff5ed8 100644 --- a/src/cmd/compile/internal/syntax/testdata/tparams.go2 +++ b/src/cmd/compile/internal/syntax/testdata/tparams.go2 @@ -4,8 +4,8 @@ package p -type t[ /* ERROR type parameters must be named */ a, b] struct{} -type t[a t, b t, /* ERROR type parameters must be named */ c] struct{} +type t[a, b /* ERROR missing type constraint */ ] struct{} +type t[a t, b t, c /* ERROR missing type constraint */ ] struct{} type t struct { t [n]byte t[a] @@ -18,5 +18,5 @@ type t interface { } func f[ /* ERROR empty type parameter list */ ]() -func f[ /* ERROR type parameters must be named */ a, b]() -func f[a t, b t, /* ERROR type parameters must be named */ c]() +func f[a, b /* ERROR missing type constraint */ ]() +func f[a t, b t, c /* ERROR missing type constraint */ ]() diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 index 56e90942ab..2c4b6610fe 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 @@ -5,4 +5,4 @@ package p // don't crash -func T /* ERROR missing */ [P /* ERROR named */ ] m /* ERROR m */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ +func T /* ERROR missing */ [P] /* ERROR missing */ m /* ERROR unexpected */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */ -- cgit v1.2.3-54-g00ecf From 426ff3746fb2ebb777e32572b6eda5e19263ace9 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 9 Sep 2021 14:04:43 -0700 Subject: cmd/cgo, runtime/cgo: avoid GCC/clang conversion warnings Add explicit conversions to avoid warnings from -Wsign-conversion and -Wshorten-64-to-32. Also avoid runtime errors from -fsanitize=undefined. Fixes #48121 Change-Id: I29dc8d976884fc42826392c10f1e1759bb1a3989 Reviewed-on: https://go-review.googlesource.com/c/go/+/348739 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/cgo/out.go | 4 ++-- src/runtime/cgo/gcc_sigaction.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 3badd73f79..ee989b95e5 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1458,10 +1458,10 @@ const gccProlog = ` (have a negative array count) and an inscrutable error will come out of the compiler and hopefully mention "name". */ -#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1]; +#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL]; /* Check at compile time that the sizes we use match our expectations. */ -#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n) +#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n) __cgo_size_assert(char, 1) __cgo_size_assert(short, 2) diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c index dd283151f1..fcf1e50740 100644 --- a/src/runtime/cgo/gcc_sigaction.c +++ b/src/runtime/cgo/gcc_sigaction.c @@ -49,13 +49,13 @@ x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *ol sigemptyset(&act.sa_mask); for (i = 0; i < 8 * sizeof(goact->mask); i++) { if (goact->mask & ((uint64_t)(1)<flags & ~SA_RESTORER; + act.sa_flags = (int)(goact->flags & ~(uint64_t)SA_RESTORER); } - ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); + ret = sigaction((int)signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); if (ret == -1) { // runtime.rt_sigaction expects _cgo_sigaction to return errno on error. _cgo_tsan_release(); @@ -70,11 +70,11 @@ x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *ol } oldgoact->mask = 0; for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) { - if (sigismember(&oldact.sa_mask, i+1) == 1) { + if (sigismember(&oldact.sa_mask, (int)(i+1)) == 1) { oldgoact->mask |= (uint64_t)(1)<flags = oldact.sa_flags; + oldgoact->flags = (uint64_t)oldact.sa_flags; } _cgo_tsan_release(); -- cgit v1.2.3-54-g00ecf From 1a708bcf1d17171056a42ec1597ca8848c854d2a Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 9 Sep 2021 14:42:42 -0700 Subject: cmd/compile: don't crash while reporting invalid alias cycle Add a missing nil check in the formatting code for expression nodes. Matches the nil checks in the same code. Fixes #48301. Change-Id: Ia9bfd3535254a94996ee190b544d95e15433d252 Reviewed-on: https://go-review.googlesource.com/c/go/+/348740 Trust: Robert Griesemer Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/ir/fmt.go | 2 +- test/fixedbugs/issue48301.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue48301.go diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index d19fe453ef..22fbf39975 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -559,7 +559,7 @@ func exprFmt(n Node, s fmt.State, prec int) { } nprec := OpPrec[n.Op()] - if n.Op() == OTYPE && n.Type().IsPtr() { + if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() { nprec = OpPrec[ODEREF] } diff --git a/test/fixedbugs/issue48301.go b/test/fixedbugs/issue48301.go new file mode 100644 index 0000000000..46fe779a42 --- /dev/null +++ b/test/fixedbugs/issue48301.go @@ -0,0 +1,13 @@ +// errorcheck -G=0 + +// Copyright 2021 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. + +// Don't crash while reporting the error. + +package p + +func _() { + type T = T // ERROR "T uses T" +} -- cgit v1.2.3-54-g00ecf From b32209d22d0418594bd60af152b0f2c90c677941 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 9 Sep 2021 15:55:05 -0700 Subject: cmd/compile: fix test case for unified IR (fix build) For #48301. Change-Id: Ie5f57dcce86773c06c5140abf13a6cfff79eb323 Reviewed-on: https://go-review.googlesource.com/c/go/+/348743 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot --- test/fixedbugs/issue48301.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixedbugs/issue48301.go b/test/fixedbugs/issue48301.go index 46fe779a42..1ff9ffb9a0 100644 --- a/test/fixedbugs/issue48301.go +++ b/test/fixedbugs/issue48301.go @@ -9,5 +9,5 @@ package p func _() { - type T = T // ERROR "T uses T" + type T = T // ERROR "T uses T|invalid recursive type T" } -- cgit v1.2.3-54-g00ecf From 2091bd3f26e5143bd050833b3558893e1bc34625 Mon Sep 17 00:00:00 2001 From: fanzha02 Date: Wed, 21 Oct 2020 18:51:42 +0800 Subject: cmd/compile: simiplify arm64 bitfield optimizations In some rewrite rules for arm64 bitfield optimizations, the bitfield lsb value and the bitfield width value are related to datasize, some of them use datasize directly to check the bitfield lsb value is valid, to get the bitfiled width value, but some of them call isARM64BFMask() and arm64BFWidth() functions. In order to be consistent, this patch changes them all to use datasize. Besides, this patch sorts the codegen test cases. Run the "toolstash-check -all" command and find one inconsistent code is as the following. new: src/math/fma.go:104 BEQ 247 master: src/math/fma.go:104 BEQ 248 The above inconsistence is due to this patch changing the range of the field lsb value in "UBFIZ" optimization rules from "lc+(32|16|8)<64" to "lc<64", so that the following code is generated as "UBFIZ". The logical of changed code is still correct. The code of src/math/fma.go:160: const uvinf = 0x7FF0000000000000 func FMA(a, b uint32) float64 { ps := a+b return Float64frombits(uint64(ps)<<63 | uvinf) } The new assembly code: TEXT "".FMA(SB), LEAF|NOFRAME|ABIInternal, $0-16 MOVWU "".a(FP), R0 MOVWU "".b+4(FP), R1 ADD R1, R0, R0 UBFIZ $63, R0, $1, R0 ORR $9218868437227405312, R0, R0 MOVD R0, "".~r2+8(FP) RET (R30) The master assembly code: TEXT "".FMA(SB), LEAF|NOFRAME|ABIInternal, $0-16 MOVWU "".a(FP), R0 MOVWU "".b+4(FP), R1 ADD R1, R0, R0 MOVWU R0, R0 LSL $63, R0, R0 ORR $9218868437227405312, R0, R0 MOVD R0, "".~r2+8(FP) RET (R30) Change-Id: I9061104adfdfd3384d0525327ae1e5c8b0df5c35 Reviewed-on: https://go-review.googlesource.com/c/go/+/265038 Trust: fannie zhang Run-TryBot: fannie zhang TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/ssa/gen/ARM64.rules | 47 ++++--- src/cmd/compile/internal/ssa/rewrite.go | 2 +- src/cmd/compile/internal/ssa/rewriteARM64.go | 196 +++++++++++++-------------- test/codegen/bitfield.go | 144 ++++++++++++++------ 4 files changed, 218 insertions(+), 171 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index ca9d4a4f01..d99487c31b 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1824,6 +1824,7 @@ // sbfiz // (x << lc) >> rc (SRAconst [rc] (SLLconst [lc] x)) && lc > rc => (SBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) +// int64(x << lc) (MOVWreg (SLLconst [lc] x)) && lc < 32 => (SBFIZ [armBFAuxInt(lc, 32-lc)] x) (MOVHreg (SLLconst [lc] x)) && lc < 16 => (SBFIZ [armBFAuxInt(lc, 16-lc)] x) (MOVBreg (SLLconst [lc] x)) && lc < 8 => (SBFIZ [armBFAuxInt(lc, 8-lc)] x) @@ -1835,6 +1836,7 @@ // sbfx // (x << lc) >> rc (SRAconst [rc] (SLLconst [lc] x)) && lc <= rc => (SBFX [armBFAuxInt(rc-lc, 64-rc)] x) +// int64(x) >> rc (SRAconst [rc] (MOVWreg x)) && rc < 32 => (SBFX [armBFAuxInt(rc, 32-rc)] x) (SRAconst [rc] (MOVHreg x)) && rc < 16 => (SBFX [armBFAuxInt(rc, 16-rc)] x) (SRAconst [rc] (MOVBreg x)) && rc < 8 => (SBFX [armBFAuxInt(rc, 8-rc)] x) @@ -1851,42 +1853,43 @@ => (SBFX [armBFAuxInt(sc-bfc.getARM64BFlsb(), bfc.getARM64BFlsb()+bfc.getARM64BFwidth()-sc)] x) // ubfiz +// (x << lc) >> rc +(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) +// uint64(x) << lc +(SLLconst [lc] (MOVWUreg x)) => (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) +(SLLconst [lc] (MOVHUreg x)) => (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) +(SLLconst [lc] (MOVBUreg x)) => (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) +// uint64(x << lc) +(MOVWUreg (SLLconst [lc] x)) && lc < 32 => (UBFIZ [armBFAuxInt(lc, 32-lc)] x) +(MOVHUreg (SLLconst [lc] x)) && lc < 16 => (UBFIZ [armBFAuxInt(lc, 16-lc)] x) +(MOVBUreg (SLLconst [lc] x)) && lc < 8 => (UBFIZ [armBFAuxInt(lc, 8-lc)] x) + +// merge ANDconst into ubfiz // (x & ac) << sc (SLLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, 0) => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) -(SLLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFIZ [armBFAuxInt(sc, 32)] x) -(SLLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFIZ [armBFAuxInt(sc, 16)] x) -(SLLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFIZ [armBFAuxInt(sc, 8)] x) // (x << sc) & ac (ANDconst [ac] (SLLconst [sc] x)) && isARM64BFMask(sc, ac, sc) => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) -(MOVWUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) -(MOVHUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) -(MOVBUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, sc) - => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) -// (x << lc) >> rc -(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x) // ubfx +// (x << lc) >> rc +(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) +// uint64(x) >> rc +(SRLconst [rc] (MOVWUreg x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32-rc)] x) +(SRLconst [rc] (MOVHUreg x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16-rc)] x) +(SRLconst [rc] (MOVBUreg x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8-rc)] x) +// uint64(x >> rc) +(MOVWUreg (SRLconst [rc] x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32)] x) +(MOVHUreg (SRLconst [rc] x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16)] x) +(MOVBUreg (SRLconst [rc] x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8)] x) +// merge ANDconst into ubfx // (x >> sc) & ac (ANDconst [ac] (SRLconst [sc] x)) && isARM64BFMask(sc, ac, 0) => (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) -(MOVWUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFX [armBFAuxInt(sc, 32)] x) -(MOVHUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFX [armBFAuxInt(sc, 16)] x) -(MOVBUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFX [armBFAuxInt(sc, 8)] x) // (x & ac) >> sc (SRLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, sc) => (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) -(SRLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) -(SRLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) -(SRLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, sc) - => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) -// (x << lc) >> rc -(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) // merge ubfx and zerso-extension into ubfx (MOVWUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (UBFX [bfc] x) diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index a997050ee2..162d42773a 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -1572,7 +1572,7 @@ func armBFAuxInt(lsb, width int64) arm64BitField { if lsb < 0 || lsb > 63 { panic("ARM(64) bit field lsb constant out of range") } - if width < 1 || width > 64 { + if width < 1 || lsb+width > 64 { panic("ARM(64) bit field width constant out of range") } return arm64BitField(width | lsb<<8) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index c62ff73c59..83dd771436 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -7157,37 +7157,37 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVBUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<8-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) + // match: (MOVBUreg (SLLconst [lc] x)) + // cond: lc < 8 + // result: (UBFIZ [armBFAuxInt(lc, 8-lc)] x) for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, sc)) { + if !(lc < 8) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 8-lc)) v.AddArg(x) return true } - // match: (MOVBUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<8-1, 0) - // result: (UBFX [armBFAuxInt(sc, 8)] x) + // match: (MOVBUreg (SRLconst [rc] x)) + // cond: rc < 8 + // result: (UBFX [armBFAuxInt(rc, 8)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, 0)) { + if !(rc < 8) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8)) v.AddArg(x) return true } @@ -10703,37 +10703,37 @@ func rewriteValueARM64_OpARM64MOVHUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVHUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<16-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) + // match: (MOVHUreg (SLLconst [lc] x)) + // cond: lc < 16 + // result: (UBFIZ [armBFAuxInt(lc, 16-lc)] x) for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, sc)) { + if !(lc < 16) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 16-lc)) v.AddArg(x) return true } - // match: (MOVHUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<16-1, 0) - // result: (UBFX [armBFAuxInt(sc, 16)] x) + // match: (MOVHUreg (SRLconst [rc] x)) + // cond: rc < 16 + // result: (UBFX [armBFAuxInt(rc, 16)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, 0)) { + if !(rc < 16) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16)) v.AddArg(x) return true } @@ -12849,37 +12849,37 @@ func rewriteValueARM64_OpARM64MOVWUreg(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } - // match: (MOVWUreg (SLLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<32-1, sc) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) + // match: (MOVWUreg (SLLconst [lc] x)) + // cond: lc < 32 + // result: (UBFIZ [armBFAuxInt(lc, 32-lc)] x) for { if v_0.Op != OpARM64SLLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, sc)) { + if !(lc < 32) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 32-lc)) v.AddArg(x) return true } - // match: (MOVWUreg (SRLconst [sc] x)) - // cond: isARM64BFMask(sc, 1<<32-1, 0) - // result: (UBFX [armBFAuxInt(sc, 32)] x) + // match: (MOVWUreg (SRLconst [rc] x)) + // cond: rc < 32 + // result: (UBFX [armBFAuxInt(rc, 32)] x) for { if v_0.Op != OpARM64SRLconst { break } - sc := auxIntToInt64(v_0.AuxInt) + rc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, 0)) { + if !(rc < 32) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32)) v.AddArg(x) return true } @@ -20130,72 +20130,60 @@ func rewriteValueARM64_OpARM64SLLconst(v *Value) bool { v.AddArg(x) return true } - // match: (SLLconst [sc] (ANDconst [ac] x)) - // cond: isARM64BFMask(sc, ac, 0) - // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) + // match: (SLLconst [lc] (MOVWUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64ANDconst { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVWUreg { break } - ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, ac, 0)) { - break - } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVWUreg x)) - // cond: isARM64BFMask(sc, 1<<32-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 32)] x) + // match: (SLLconst [lc] (MOVHUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64MOVWUreg { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVHUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, 0)) { - break - } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVHUreg x)) - // cond: isARM64BFMask(sc, 1<<16-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 16)] x) + // match: (SLLconst [lc] (MOVBUreg x)) + // result: (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64MOVHUreg { + lc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVBUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, 0)) { - break - } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc))) v.AddArg(x) return true } - // match: (SLLconst [sc] (MOVBUreg x)) - // cond: isARM64BFMask(sc, 1<<8-1, 0) - // result: (UBFIZ [armBFAuxInt(sc, 8)] x) + // match: (SLLconst [sc] (ANDconst [ac] x)) + // cond: isARM64BFMask(sc, ac, 0) + // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x) for { sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64MOVBUreg { + if v_0.Op != OpARM64ANDconst { break } + ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, 0)) { + if !(isARM64BFMask(sc, ac, 0)) { break } v.reset(OpARM64UBFIZ) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0))) v.AddArg(x) return true } @@ -20488,90 +20476,90 @@ func rewriteValueARM64_OpARM64SRLconst(v *Value) bool { v.AddArg(x) return true } - // match: (SRLconst [sc] (ANDconst [ac] x)) - // cond: isARM64BFMask(sc, ac, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) + // match: (SRLconst [rc] (SLLconst [lc] x)) + // cond: lc < rc + // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64ANDconst { + rc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst { break } - ac := auxIntToInt64(v_0.AuxInt) + lc := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(isARM64BFMask(sc, ac, sc)) { + if !(lc < rc) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVWUreg x)) - // cond: isARM64BFMask(sc, 1<<32-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x) + // match: (SRLconst [rc] (MOVWUreg x)) + // cond: rc < 32 + // result: (UBFX [armBFAuxInt(rc, 32-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVWUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<32-1, sc)) { + if !(rc < 32) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVHUreg x)) - // cond: isARM64BFMask(sc, 1<<16-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x) + // match: (SRLconst [rc] (MOVHUreg x)) + // cond: rc < 16 + // result: (UBFX [armBFAuxInt(rc, 16-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVHUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<16-1, sc)) { + if !(rc < 16) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16-rc)) v.AddArg(x) return true } - // match: (SRLconst [sc] (MOVBUreg x)) - // cond: isARM64BFMask(sc, 1<<8-1, sc) - // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x) + // match: (SRLconst [rc] (MOVBUreg x)) + // cond: rc < 8 + // result: (UBFX [armBFAuxInt(rc, 8-rc)] x) for { - sc := auxIntToInt64(v.AuxInt) + rc := auxIntToInt64(v.AuxInt) if v_0.Op != OpARM64MOVBUreg { break } x := v_0.Args[0] - if !(isARM64BFMask(sc, 1<<8-1, sc)) { + if !(rc < 8) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8-rc)) v.AddArg(x) return true } - // match: (SRLconst [rc] (SLLconst [lc] x)) - // cond: lc < rc - // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x) + // match: (SRLconst [sc] (ANDconst [ac] x)) + // cond: isARM64BFMask(sc, ac, sc) + // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x) for { - rc := auxIntToInt64(v.AuxInt) - if v_0.Op != OpARM64SLLconst { + sc := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64ANDconst { break } - lc := auxIntToInt64(v_0.AuxInt) + ac := auxIntToInt64(v_0.AuxInt) x := v_0.Args[0] - if !(lc < rc) { + if !(isARM64BFMask(sc, ac, sc)) { break } v.reset(OpARM64UBFX) - v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc)) + v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc))) v.AddArg(x) return true } diff --git a/test/codegen/bitfield.go b/test/codegen/bitfield.go index 3ed9cfe603..8327da6cf8 100644 --- a/test/codegen/bitfield.go +++ b/test/codegen/bitfield.go @@ -77,11 +77,13 @@ func bfxil2(x, y uint64) uint64 { } // sbfiz +// merge shifts into sbfiz: (x << lc) >> rc && lc > rc. func sbfiz1(x int64) int64 { // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" return (x << 4) >> 3 } +// merge shift and sign-extension into sbfiz. func sbfiz2(x int32) int64 { return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]29",-"LSL" } @@ -94,6 +96,8 @@ func sbfiz4(x int8) int64 { return int64(x << 3) // arm64:"SBFIZ\t[$]3, R[0-9]+, [$]5",-"LSL" } +// sbfiz combinations. +// merge shift with sbfiz into sbfiz. func sbfiz5(x int32) int32 { // arm64:"SBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" return (x << 4) >> 3 @@ -112,6 +116,7 @@ func sbfiz8(x int32) int64 { } // sbfx +// merge shifts into sbfx: (x << lc) >> rc && lc <= rc. func sbfx1(x int64) int64 { return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]60",-"LSL",-"ASR" } @@ -120,6 +125,7 @@ func sbfx2(x int64) int64 { return (x << 60) >> 60 // arm64:"SBFX\tZR, R[0-9]+, [$]4",-"LSL",-"ASR" } +// merge shift and sign-extension into sbfx. func sbfx3(x int32) int64 { return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]29",-"ASR" } @@ -132,131 +138,181 @@ func sbfx5(x int8) int64 { return int64(x) >> 3 // arm64:"SBFX\t[$]3, R[0-9]+, [$]5",-"ASR" } -func sbfx6(x int32) int32 { +func sbfx6(x int32) int64 { + return int64(x >> 30) // arm64:"SBFX\t[$]30, R[0-9]+, [$]2" +} + +func sbfx7(x int16) int64 { + return int64(x >> 10) // arm64:"SBFX\t[$]10, R[0-9]+, [$]6" +} + +func sbfx8(x int8) int64 { + return int64(x >> 5) // arm64:"SBFX\t[$]5, R[0-9]+, [$]3" +} + +// sbfx combinations. +// merge shifts with sbfiz into sbfx. +func sbfx9(x int32) int32 { return (x << 3) >> 4 // arm64:"SBFX\t[$]1, R[0-9]+, [$]28",-"LSL",-"ASR" } // merge sbfx and sign-extension into sbfx. -func sbfx7(x int32) int64 { +func sbfx10(x int32) int64 { c := x + 5 return int64(c >> 20) // arm64"SBFX\t[$]20, R[0-9]+, [$]12",-"MOVW\tR[0-9]+, R[0-9]+" } // ubfiz +// merge shifts into ubfiz: (x<>rc && lc>rc func ubfiz1(x uint64) uint64 { - // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND" - // s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND" - return (x & 0xfff) << 3 -} - -func ubfiz2(x uint64) uint64 { - // arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND" - // s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND" - return (x << 4) & 0xfff0 + // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR" + // s390x:"RISBGZ\t[$]3, [$]62, [$]1, ",-"SLD",-"SRD" + return (x << 4) >> 3 } -func ubfiz3(x uint32) uint64 { +// merge shift and zero-extension into ubfiz. +func ubfiz2(x uint32) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]32",-"LSL" } -func ubfiz4(x uint16) uint64 { +func ubfiz3(x uint16) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]16",-"LSL" } -func ubfiz5(x uint8) uint64 { +func ubfiz4(x uint8) uint64 { return uint64(x+1) << 3 // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]8",-"LSL" } -func ubfiz6(x uint64) uint64 { - // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]60",-"LSL",-"LSR" - // s390x:"RISBGZ\t[$]3, [$]62, [$]1, ",-"SLD",-"SRD" - return (x << 4) >> 3 +func ubfiz5(x uint8) uint64 { + return uint64(x) << 60 // arm64:"UBFIZ\t[$]60, R[0-9]+, [$]4",-"LSL" +} + +func ubfiz6(x uint32) uint64 { + return uint64(x << 30) // arm64:"UBFIZ\t[$]30, R[0-9]+, [$]2", +} + +func ubfiz7(x uint16) uint64 { + return uint64(x << 10) // arm64:"UBFIZ\t[$]10, R[0-9]+, [$]6", +} + +func ubfiz8(x uint8) uint64 { + return uint64(x << 7) // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]1", +} + +// merge ANDconst into ubfiz. +func ubfiz9(x uint64) uint64 { + // arm64:"UBFIZ\t[$]3, R[0-9]+, [$]12",-"LSL",-"AND" + // s390x:"RISBGZ\t[$]49, [$]60, [$]3,",-"SLD",-"AND" + return (x & 0xfff) << 3 +} + +func ubfiz10(x uint64) uint64 { + // arm64:"UBFIZ\t[$]4, R[0-9]+, [$]12",-"LSL",-"AND" + // s390x:"RISBGZ\t[$]48, [$]59, [$]4,",-"SLD",-"AND" + return (x << 4) & 0xfff0 } -func ubfiz7(x uint32) uint32 { +// ubfiz combinations +func ubfiz11(x uint32) uint32 { // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]28",-"LSL",-"LSR" return (x << 4) >> 3 } -func ubfiz8(x uint64) uint64 { +func ubfiz12(x uint64) uint64 { // arm64:"UBFIZ\t[$]1, R[0-9]+, [$]20",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]43, [$]62, [$]1, ",-"SLD",-"SRD",-"AND" return ((x & 0xfffff) << 4) >> 3 } -func ubfiz9(x uint64) uint64 { +func ubfiz13(x uint64) uint64 { // arm64:"UBFIZ\t[$]5, R[0-9]+, [$]13",-"LSL",-"LSR",-"AND" return ((x << 3) & 0xffff) << 2 } -func ubfiz10(x uint64) uint64 { +func ubfiz14(x uint64) uint64 { // arm64:"UBFIZ\t[$]7, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]45, [$]56, [$]7, ",-"SLD",-"SRD",-"AND" return ((x << 5) & (0xfff << 5)) << 2 } // ubfx +// merge shifts into ubfx: (x<>rc && lc> 25) & 1023 -} - -func ubfx2(x uint64) uint64 { - // arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND" - // s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND" - return (x & 0x0ff0) >> 4 + // arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR" + // s390x:"RISBGZ\t[$]2, [$]63, [$]63,",-"SLD",-"SRD" + return (x << 1) >> 2 } -func ubfx3(x uint32) uint64 { +// merge shift and zero-extension into ubfx. +func ubfx2(x uint32) uint64 { return uint64(x >> 15) // arm64:"UBFX\t[$]15, R[0-9]+, [$]17",-"LSR" } -func ubfx4(x uint16) uint64 { +func ubfx3(x uint16) uint64 { return uint64(x >> 9) // arm64:"UBFX\t[$]9, R[0-9]+, [$]7",-"LSR" } -func ubfx5(x uint8) uint64 { +func ubfx4(x uint8) uint64 { return uint64(x >> 3) // arm64:"UBFX\t[$]3, R[0-9]+, [$]5",-"LSR" } -func ubfx6(x uint64) uint64 { - // arm64:"UBFX\t[$]1, R[0-9]+, [$]62",-"LSL",-"LSR" - // s390x:"RISBGZ\t[$]2, [$]63, [$]63,",-"SLD",-"SRD" - return (x << 1) >> 2 +func ubfx5(x uint32) uint64 { + return uint64(x) >> 30 // arm64:"UBFX\t[$]30, R[0-9]+, [$]2" +} + +func ubfx6(x uint16) uint64 { + return uint64(x) >> 10 // arm64:"UBFX\t[$]10, R[0-9]+, [$]6" +} + +func ubfx7(x uint8) uint64 { + return uint64(x) >> 3 // arm64:"UBFX\t[$]3, R[0-9]+, [$]5" +} + +// merge ANDconst into ubfx. +func ubfx8(x uint64) uint64 { + // arm64:"UBFX\t[$]25, R[0-9]+, [$]10",-"LSR",-"AND" + // s390x:"RISBGZ\t[$]54, [$]63, [$]39, ",-"SRD",-"AND" + return (x >> 25) & 1023 } -func ubfx7(x uint32) uint32 { +func ubfx9(x uint64) uint64 { + // arm64:"UBFX\t[$]4, R[0-9]+, [$]8",-"LSR",-"AND" + // s390x:"RISBGZ\t[$]56, [$]63, [$]60, ",-"SRD",-"AND" + return (x & 0x0ff0) >> 4 +} + +// ubfx combinations. +func ubfx10(x uint32) uint32 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]30",-"LSL",-"LSR" return (x << 1) >> 2 } -func ubfx8(x uint64) uint64 { +func ubfx11(x uint64) uint64 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]12",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]52, [$]63, [$]63,",-"SLD",-"SRD",-"AND" return ((x << 1) >> 2) & 0xfff } -func ubfx9(x uint64) uint64 { +func ubfx12(x uint64) uint64 { // arm64:"UBFX\t[$]4, R[0-9]+, [$]11",-"LSL",-"LSR",-"AND" // s390x:"RISBGZ\t[$]53, [$]63, [$]60, ",-"SLD",-"SRD",-"AND" return ((x >> 3) & 0xfff) >> 1 } -func ubfx10(x uint64) uint64 { +func ubfx13(x uint64) uint64 { // arm64:"UBFX\t[$]5, R[0-9]+, [$]56",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]8, [$]63, [$]59, ",-"SLD",-"SRD" return ((x >> 2) << 5) >> 8 } -func ubfx11(x uint64) uint64 { +func ubfx14(x uint64) uint64 { // arm64:"UBFX\t[$]1, R[0-9]+, [$]19",-"LSL",-"LSR" // s390x:"RISBGZ\t[$]45, [$]63, [$]63, ",-"SLD",-"SRD",-"AND" return ((x & 0xfffff) << 3) >> 4 } // merge ubfx and zero-extension into ubfx. -func ubfx12(x uint64) bool { +func ubfx15(x uint64) bool { midr := x + 10 part_num := uint16((midr >> 4) & 0xfff) if part_num == 0xd0c { // arm64:"UBFX\t[$]4, R[0-9]+, [$]12",-"MOVHU\tR[0-9]+, R[0-9]+" -- cgit v1.2.3-54-g00ecf From c69f5c0d7632381dfc6dc78f0af4f54e7673176d Mon Sep 17 00:00:00 2001 From: Michael Munday Date: Thu, 9 Sep 2021 23:47:14 +0100 Subject: cmd/compile: add support for Abs and Copysign intrinsics on riscv64 Also, add the FABSS and FABSD pseudo instructions to the assembler. The compiler could use FSGNJX[SD] directly but there doesn't seem to be much advantage to doing so and the pseudo instructions are easier to understand. Change-Id: Ie8825b8aa8773c69cc4f07a32ef04abf4061d80d Reviewed-on: https://go-review.googlesource.com/c/go/+/348989 Trust: Michael Munday Run-TryBot: Michael Munday TryBot-Result: Go Bot Reviewed-by: Joel Sing --- src/cmd/asm/internal/asm/testdata/riscv64.s | 2 ++ src/cmd/compile/internal/riscv64/ssa.go | 5 +++-- src/cmd/compile/internal/ssa/gen/RISCV64.rules | 4 ++++ src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 2 ++ src/cmd/compile/internal/ssa/opGen.go | 29 ++++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewriteRISCV64.go | 6 ++++++ src/cmd/compile/internal/ssagen/ssa.go | 4 ++-- src/cmd/internal/obj/riscv/anames.go | 2 ++ src/cmd/internal/obj/riscv/cpu.go | 2 ++ src/cmd/internal/obj/riscv/obj.go | 10 +++++++++ test/codegen/math.go | 4 ++++ 11 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index 1977d92f62..64b94a2a04 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -382,10 +382,12 @@ start: SNEZ X15, X15 // b337f000 // F extension + FABSS F0, F1 // d3200020 FNEGS F0, F1 // d3100020 FNES F0, F1, X7 // d3a300a093c31300 // D extension + FABSD F0, F1 // d3200022 FNEGD F0, F1 // d3100022 FNED F0, F1, X5 // d3a200a293c21200 FLTD F0, F1, X5 // d39200a2 diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go index 30b6d96a89..e400ca1ffe 100644 --- a/src/cmd/compile/internal/riscv64/ssa.go +++ b/src/cmd/compile/internal/riscv64/ssa.go @@ -272,7 +272,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS, ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES, ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD, - ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED: + ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED, + ssa.OpRISCV64FSGNJD: r := v.Reg() r1 := v.Args[0].Reg() r2 := v.Args[1].Reg() @@ -329,7 +330,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.SetRestArgs([]obj.Addr{{Type: obj.TYPE_REG, Reg: r3}}) p.To.Type = obj.TYPE_REG p.To.Reg = r - case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, + case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD, ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX, ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS, ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD, diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules index b711550186..aa7c452d05 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules @@ -96,6 +96,10 @@ (Sqrt ...) => (FSQRTD ...) (Sqrt32 ...) => (FSQRTS ...) +(Copysign ...) => (FSGNJD ...) + +(Abs ...) => (FABSD ...) + (FMA ...) => (FMADDD ...) // Sign and zero extension. diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index de189e4c60..ac1bcd2a06 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -432,6 +432,8 @@ func init() { {name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64"}, // -(arg0 * arg1) - arg2 {name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"}, // sqrt(arg0) {name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"}, // -arg0 + {name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", typ: "Float64"}, // abs(arg0) + {name: "FSGNJD", argLength: 2, reg: fp21, asm: "FSGNJD", typ: "Float64"}, // copy sign of arg1 to arg0 {name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"}, // reinterpret arg0 as float {name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"}, // float64(low 32 bits of arg0) {name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"}, // float64(arg0) diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 573559db70..1ca99c1ba9 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2183,6 +2183,8 @@ const ( OpRISCV64FNMSUBD OpRISCV64FSQRTD OpRISCV64FNEGD + OpRISCV64FABSD + OpRISCV64FSGNJD OpRISCV64FMVDX OpRISCV64FCVTDW OpRISCV64FCVTDL @@ -29187,6 +29189,33 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "FABSD", + argLen: 1, + asm: riscv.AFABSD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FSGNJD", + argLen: 2, + asm: riscv.AFSGNJD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, { name: "FMVDX", argLen: 1, diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index 743ff50b0c..3a277ca369 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -8,6 +8,9 @@ import "cmd/compile/internal/types" func rewriteValueRISCV64(v *Value) bool { switch v.Op { + case OpAbs: + v.Op = OpRISCV64FABSD + return true case OpAdd16: v.Op = OpRISCV64ADD return true @@ -134,6 +137,9 @@ func rewriteValueRISCV64(v *Value) bool { case OpConvert: v.Op = OpRISCV64MOVconvert return true + case OpCopysign: + v.Op = OpRISCV64FSGNJD + return true case OpCvt32Fto32: v.Op = OpRISCV64FCVTWS return true diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 11bca89fd8..1e7eda94fc 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -4212,12 +4212,12 @@ func InitTables() { func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue1(ssa.OpAbs, types.Types[types.TFLOAT64], args[0]) }, - sys.ARM64, sys.ARM, sys.PPC64, sys.Wasm) + sys.ARM64, sys.ARM, sys.PPC64, sys.RISCV64, sys.Wasm) addF("math", "Copysign", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue2(ssa.OpCopysign, types.Types[types.TFLOAT64], args[0], args[1]) }, - sys.PPC64, sys.Wasm) + sys.PPC64, sys.RISCV64, sys.Wasm) addF("math", "FMA", func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value { return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2]) diff --git a/src/cmd/internal/obj/riscv/anames.go b/src/cmd/internal/obj/riscv/anames.go index 6581bb3402..d2a3674ebe 100644 --- a/src/cmd/internal/obj/riscv/anames.go +++ b/src/cmd/internal/obj/riscv/anames.go @@ -236,6 +236,8 @@ var Anames = []string{ "BLEZ", "BLTZ", "BNEZ", + "FABSD", + "FABSS", "FNEGD", "FNEGS", "FNED", diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 1519dc1a63..a258367ae9 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -590,6 +590,8 @@ const ( ABLEZ ABLTZ ABNEZ + AFABSD + AFABSS AFNEGD AFNEGS AFNED diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index fafde64062..62d44d8a3f 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -1998,6 +1998,16 @@ func instructionsForProg(p *obj.Prog) []*instruction { ins.as = ASLTU ins.rs1 = REG_ZERO + case AFABSS: + // FABSS rs, rd -> FSGNJXS rs, rs, rd + ins.as = AFSGNJXS + ins.rs1 = uint32(p.From.Reg) + + case AFABSD: + // FABSD rs, rd -> FSGNJXD rs, rs, rd + ins.as = AFSGNJXD + ins.rs1 = uint32(p.From.Reg) + case AFNEGS: // FNEGS rs, rd -> FSGNJNS rs, rs, rd ins.as = AFSGNJNS diff --git a/test/codegen/math.go b/test/codegen/math.go index cd573db7b3..df2ebd79e1 100644 --- a/test/codegen/math.go +++ b/test/codegen/math.go @@ -73,6 +73,7 @@ func abs(x, y float64) { // s390x:"LPDFR\t",-"MOVD\t" (no integer load/store) // ppc64:"FABS\t" // ppc64le:"FABS\t" + // riscv64:"FABSD\t" // wasm:"F64Abs" // arm/6:"ABSD\t" sink64[0] = math.Abs(x) @@ -96,6 +97,7 @@ func copysign(a, b, c float64) { // s390x:"CPSDR",-"MOVD" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" // wasm:"F64Copysign" sink64[0] = math.Copysign(a, b) @@ -103,6 +105,7 @@ func copysign(a, b, c float64) { // s390x:"LNDFR\t",-"MOVD\t" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" // arm64:"ORR", -"AND" sink64[1] = math.Copysign(c, -1) @@ -115,6 +118,7 @@ func copysign(a, b, c float64) { // s390x:"CPSDR\t",-"MOVD\t" (no integer load/store) // ppc64:"FCPSGN" // ppc64le:"FCPSGN" + // riscv64:"FSGNJD" sink64[3] = math.Copysign(-1, c) } -- cgit v1.2.3-54-g00ecf From 90c5660616d7f006ca62adfec49310bf40e3b354 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 26 Apr 2021 22:32:21 +0900 Subject: embed: guarantee the returned file of FS.Open implements io.Seeker Fixes golang/go#45745 Change-Id: Ib49a9605a38074f544a5d28116862e191cea8c0f Reviewed-on: https://go-review.googlesource.com/c/go/+/313352 Run-TryBot: Hajime Hoshi TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Than McIntosh --- src/embed/embed.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/embed/embed.go b/src/embed/embed.go index 5dcd7f227d..f87cc5b963 100644 --- a/src/embed/embed.go +++ b/src/embed/embed.go @@ -291,6 +291,8 @@ func (f FS) readDir(dir string) []file { } // Open opens the named file for reading and returns it as an fs.File. +// +// The returned file implements io.Seeker when the file is not a directory. func (f FS) Open(name string) (fs.File, error) { file := f.lookup(name) if file == nil { @@ -338,6 +340,10 @@ type openFile struct { offset int64 // current read offset } +var ( + _ io.Seeker = (*openFile)(nil) +) + func (f *openFile) Close() error { return nil } func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil } -- cgit v1.2.3-54-g00ecf From 96ab854ab03f6a21c676c2a7aa9c4ad933892a42 Mon Sep 17 00:00:00 2001 From: Zheng Xu Date: Wed, 1 Sep 2021 13:48:48 +0800 Subject: cmd/compile/internal: better AST line highlight in ssa.html We tend to set div class with the line number in HTML AST nodes. So that the AST nodes can be highlighted with corresponding source and ssa ir. The pure AST text dump is created first. And then it is parsed and written to the HTML file. CL 275785 changed the format of the line information in AST node dump, which makes the HTMLWriter fail to parse the line information. This CL updates the code in HTMLWriter to align with the format of AST node dump. Fix #48133 Change-Id: I2b56fc5e3e9771456d91f22caf23a427c235eb12 Reviewed-on: https://go-review.googlesource.com/c/go/+/347269 Reviewed-by: Cherry Mui Trust: Than McIntosh --- src/cmd/compile/internal/ir/fmt.go | 1 + src/cmd/compile/internal/ssa/html.go | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index 22fbf39975..a99cb5ed98 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -1147,6 +1147,7 @@ func dumpNodeHeader(w io.Writer, n Node) { } // TODO(mdempsky): Print line pragma details too. file := filepath.Base(pos.Filename()) + // Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync. fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col()) } } diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 6fd898636c..d9a78b3962 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -903,15 +903,12 @@ func (w *HTMLWriter) WriteAST(phase string, buf *bytes.Buffer) { if strings.HasPrefix(l, "buildssa") { escaped = fmt.Sprintf("%v", l) } else { - // Parse the line number from the format l(123). - idx := strings.Index(l, " l(") - if idx != -1 { - subl := l[idx+3:] - idxEnd := strings.Index(subl, ")") - if idxEnd != -1 { - if _, err := strconv.Atoi(subl[:idxEnd]); err == nil { - lineNo = subl[:idxEnd] - } + // Parse the line number from the format file:line:col. + // See the implementation in ir/fmt.go:dumpNodeHeader. + sl := strings.Split(l, ":") + if len(sl) >= 3 { + if _, err := strconv.Atoi(sl[len(sl)-2]); err == nil { + lineNo = sl[len(sl)-2] } } escaped = html.EscapeString(l) -- cgit v1.2.3-54-g00ecf From da1aa650536b188c4dce287003a6f46b0dc4bdd5 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 9 Sep 2021 15:43:19 -0700 Subject: cmd/compile/internal/syntax: correct follow token for type parameter lists When parsing a type parameter declaration, parts of the code still expected a ) as closing token. Use the correct follow token ) or ] depending on parameter list kind. Also, consistently use tokstring (not tok.String()) for user-facing (error) messages. Follow-up on comment in CL 348730. For #43527. Change-Id: Ib1d4feb526771a1668a54c3bb7a671f6c8a65940 Reviewed-on: https://go-review.googlesource.com/c/go/+/348742 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/syntax/parser.go | 14 ++++++++------ src/cmd/compile/internal/syntax/testdata/tparams.go2 | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index c836a21c2f..82cb06b180 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -276,7 +276,9 @@ func (p *parser) syntaxErrorAt(pos Pos, msg string) { } // tokstring returns the English word for selected punctuation tokens -// for more readable error messages. +// for more readable error messages. Use tokstring (not tok.String()) +// for user-facing (error) messages; use tok.String() for debugging +// output. func tokstring(tok token) string { switch tok { case _Comma: @@ -1839,7 +1841,7 @@ func (p *parser) embeddedTerm() Expr { } // ParameterDecl = [ IdentifierList ] [ "..." ] Type . -func (p *parser) paramDeclOrNil(name *Name) *Field { +func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { if trace { defer p.trace("paramDecl")() } @@ -1893,8 +1895,8 @@ func (p *parser) paramDeclOrNil(name *Name) *Field { return f } - p.syntaxError("expecting )") - p.advance(_Comma, _Rparen) + p.syntaxError("expecting " + tokstring(follow)) + p.advance(_Comma, follow) return nil } @@ -1911,7 +1913,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []* var named int // number of parameters that have an explicit name and type var typed int // number of parameters that have an explicit type end := p.list(_Comma, close, func() bool { - par := p.paramDeclOrNil(name) + par := p.paramDeclOrNil(name, close) name = nil // 1st name was consumed if present if par != nil { if debug && par.Name == nil && par.Type == nil { @@ -2211,7 +2213,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS if p.tok != _Semi { // accept potential varDecl but complain if p.got(_Var) { - p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String())) + p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", tokstring(keyword))) } init = p.simpleStmt(nil, keyword) // If we have a range clause, we are done (can only happen for keyword == _For). diff --git a/src/cmd/compile/internal/syntax/testdata/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go2 index 8e47ff5ed8..80e155bfe0 100644 --- a/src/cmd/compile/internal/syntax/testdata/tparams.go2 +++ b/src/cmd/compile/internal/syntax/testdata/tparams.go2 @@ -20,3 +20,5 @@ type t interface { func f[ /* ERROR empty type parameter list */ ]() func f[a, b /* ERROR missing type constraint */ ]() func f[a t, b t, c /* ERROR missing type constraint */ ]() + +func f[a b, /* ERROR expecting ] */ 0] () -- cgit v1.2.3-54-g00ecf From 5a94a90d84cc65a04ab44737baa406023e9b2001 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Sep 2021 22:08:14 -0700 Subject: cmd/compile/internal/types2: better error message for invalid array decls Fixes #43527. Change-Id: I988a4d49f2f54b4b1741688fb52a55bf313d39e1 Reviewed-on: https://go-review.googlesource.com/c/go/+/348731 Trust: Robert Griesemer Reviewed-by: Robert Findley --- .../internal/types2/testdata/fixedbugs/issue43527.go2 | 16 ++++++++++++++++ src/cmd/compile/internal/types2/typexpr.go | 10 ++++++++++ src/go/types/testdata/fixedbugs/issue43527.go2 | 16 ++++++++++++++++ src/go/types/typexpr.go | 10 ++++++++++ 4 files changed, 52 insertions(+) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 create mode 100644 src/go/types/testdata/fixedbugs/issue43527.go2 diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 new file mode 100644 index 0000000000..e4bcee51fe --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 @@ -0,0 +1,16 @@ +// Copyright 2021 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 p + +const L = 10 + +type ( + _ [L]struct{} + _ [A /* ERROR undeclared name A for array length */ ]struct{} + _ [B /* ERROR not an expression */ ]struct{} + _[A any] struct{} + + B int +) diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index f3db3bbba9..5aacb94a60 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -428,6 +428,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def // and returns the constant length >= 0, or a value < 0 // to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e syntax.Expr) int64 { + // If e is an undeclared identifier, the array declaration might be an + // attempt at a parameterized type declaration with missing constraint. + // Provide a better error message than just "undeclared name: X". + if name, _ := e.(*syntax.Name); name != nil && check.lookup(name.Value) == nil { + check.errorf(name, "undeclared name %s for array length", name.Value) + return -1 + } + var x operand check.expr(&x, e) if x.mode != constant_ { @@ -436,6 +444,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 { } return -1 } + if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check, Typ[Int], nil) { @@ -447,6 +456,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 { } } } + check.errorf(&x, "array length %s must be integer", &x) return -1 } diff --git a/src/go/types/testdata/fixedbugs/issue43527.go2 b/src/go/types/testdata/fixedbugs/issue43527.go2 new file mode 100644 index 0000000000..e4bcee51fe --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue43527.go2 @@ -0,0 +1,16 @@ +// Copyright 2021 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 p + +const L = 10 + +type ( + _ [L]struct{} + _ [A /* ERROR undeclared name A for array length */ ]struct{} + _ [B /* ERROR not an expression */ ]struct{} + _[A any] struct{} + + B int +) diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 6b4a3538b6..0143f53009 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -412,6 +412,14 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named // and returns the constant length >= 0, or a value < 0 // to indicate an error (and thus an unknown length). func (check *Checker) arrayLength(e ast.Expr) int64 { + // If e is an undeclared identifier, the array declaration might be an + // attempt at a parameterized type declaration with missing constraint. + // Provide a better error message than just "undeclared name: X". + if name, _ := e.(*ast.Ident); name != nil && check.lookup(name.Name) == nil { + check.errorf(name, _InvalidArrayLen, "undeclared name %s for array length", name.Name) + return -1 + } + var x operand check.expr(&x, e) if x.mode != constant_ { @@ -420,6 +428,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { } return -1 } + if isUntyped(x.typ) || isInteger(x.typ) { if val := constant.ToInt(x.val); val.Kind() == constant.Int { if representableConst(val, check, Typ[Int], nil) { @@ -431,6 +440,7 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { } } } + check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x) return -1 } -- cgit v1.2.3-54-g00ecf From 025308fe084264538f49924b3f52d8d6b6359658 Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Fri, 20 Aug 2021 18:57:45 -0500 Subject: testing: increase alternation precedence Updates handling of go test flags -run and -bench to give alternation precendence over the / delimiter. Currently, `A/B|C/D` is effectively `A/(B|C)/D` - with this change, it changes to effectively `(A/B)|(C/D)`. Fixes #39904 Change-Id: Iebe5efd8d91c72eed6351bd63b4689b0fcb0ed0f Reviewed-on: https://go-review.googlesource.com/c/go/+/343883 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Than McIntosh --- src/testing/match.go | 108 +++++++++++++++++++++++++++++++++++++--------- src/testing/match_test.go | 29 ++++++++++--- 2 files changed, 110 insertions(+), 27 deletions(-) diff --git a/src/testing/match.go b/src/testing/match.go index b18c6e7f38..d97e415765 100644 --- a/src/testing/match.go +++ b/src/testing/match.go @@ -14,34 +14,45 @@ import ( // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks. type matcher struct { - filter []string + filter filterMatch matchFunc func(pat, str string) (bool, error) mu sync.Mutex subNames map[string]int64 } +type filterMatch interface { + // matches checks the name against the receiver's pattern strings using the + // given match function. + matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) + + // verify checks that the receiver's pattern strings are valid filters by + // calling the given match function. + verify(name string, matchString func(pat, str string) (bool, error)) error +} + +// simpleMatch matches a test name if all of the pattern strings match in +// sequence. +type simpleMatch []string + +// alternationMatch matches a test name if one of the alternations match. +type alternationMatch []filterMatch + // TODO: fix test_main to avoid race and improve caching, also allowing to // eliminate this Mutex. var matchMutex sync.Mutex func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher { - var filter []string + var impl filterMatch if patterns != "" { - filter = splitRegexp(patterns) - for i, s := range filter { - filter[i] = rewrite(s) - } - // Verify filters before doing any processing. - for i, s := range filter { - if _, err := matchString(s, "non-empty"); err != nil { - fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err) - os.Exit(1) - } + impl = splitRegexp(patterns) + if err := impl.verify(name, matchString); err != nil { + fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err) + os.Exit(1) } } return &matcher{ - filter: filter, + filter: impl, matchFunc: matchString, subNames: map[string]int64{}, } @@ -60,22 +71,63 @@ func (m *matcher) fullName(c *common, subname string) (name string, ok, partial matchMutex.Lock() defer matchMutex.Unlock() + if m.filter == nil { + return name, true, false + } + // We check the full array of paths each time to allow for the case that // a pattern contains a '/'. elem := strings.Split(name, "/") - for i, s := range elem { - if i >= len(m.filter) { + ok, partial = m.filter.matches(elem, m.matchFunc) + return name, ok, partial +} + +func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) { + for i, s := range name { + if i >= len(m) { break } - if ok, _ := m.matchFunc(m.filter[i], s); !ok { - return name, false, false + if ok, _ := matchString(m[i], s); !ok { + return false, false + } + } + return true, len(name) < len(m) +} + +func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error { + for i, s := range m { + m[i] = rewrite(s) + } + // Verify filters before doing any processing. + for i, s := range m { + if _, err := matchString(s, "non-empty"); err != nil { + return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err) + } + } + return nil +} + +func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) { + for _, m := range m { + if ok, partial = m.matches(name, matchString); ok { + return ok, partial + } + } + return false, false +} + +func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error { + for i, m := range m { + if err := m.verify(name, matchString); err != nil { + return fmt.Errorf("alternation %d of %s", i, err) } } - return name, true, len(elem) < len(m.filter) + return nil } -func splitRegexp(s string) []string { - a := make([]string, 0, strings.Count(s, "/")) +func splitRegexp(s string) filterMatch { + a := make(simpleMatch, 0, strings.Count(s, "/")) + b := make(alternationMatch, 0, strings.Count(s, "|")) cs := 0 cp := 0 for i := 0; i < len(s); { @@ -103,10 +155,24 @@ func splitRegexp(s string) []string { i = 0 continue } + case '|': + if cs == 0 && cp == 0 { + a = append(a, s[:i]) + s = s[i+1:] + i = 0 + b = append(b, a) + a = make(simpleMatch, 0, len(a)) + continue + } } i++ } - return append(a, s) + + a = append(a, s) + if len(b) == 0 { + return a + } + return append(b, a) } // unique creates a unique name for the given parent and subname by affixing it diff --git a/src/testing/match_test.go b/src/testing/match_test.go index 8c09dc660f..9ceadbb31d 100644 --- a/src/testing/match_test.go +++ b/src/testing/match_test.go @@ -5,8 +5,10 @@ package testing import ( + "fmt" "reflect" "regexp" + "strings" "unicode" ) @@ -25,10 +27,11 @@ func TestIsSpace(t *T) { } func TestSplitRegexp(t *T) { - res := func(s ...string) []string { return s } + res := func(s ...string) filterMatch { return simpleMatch(s) } + alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) } testCases := []struct { pattern string - result []string + result filterMatch }{ // Correct patterns // If a regexp pattern is correct, all split regexps need to be correct @@ -49,6 +52,8 @@ func TestSplitRegexp(t *T) { {`([)/][(])`, res(`([)/][(])`)}, {"[(]/[)]", res("[(]", "[)]")}, + {"A/B|C/D", alt(res("A", "B"), res("C", "D"))}, + // Faulty patterns // Errors in original should produce at least one faulty regexp in results. {")/", res(")/")}, @@ -71,10 +76,8 @@ func TestSplitRegexp(t *T) { // needs to have an error as well. if _, err := regexp.Compile(tc.pattern); err != nil { ok := true - for _, re := range a { - if _, err := regexp.Compile(re); err != nil { - ok = false - } + if err := a.verify("", regexp.MatchString); err != nil { + ok = false } if ok { t.Errorf("%s: expected error in any of %q", tc.pattern, a) @@ -113,6 +116,10 @@ func TestMatcher(t *T) { {"TestFoo/", "TestBar", "x", false, false}, {"TestFoo/bar/baz", "TestBar", "x/bar/baz", false, false}, + {"A/B|C/D", "TestA", "B", true, false}, + {"A/B|C/D", "TestC", "D", true, false}, + {"A/B|C/D", "TestA", "C", false, false}, + // subtests only {"", "TestFoo", "x", true, false}, {"/", "TestFoo", "x", true, false}, @@ -184,3 +191,13 @@ func TestNaming(t *T) { } } } + +// GoString returns a string that is more readable than the default, which makes +// it easier to read test errors. +func (m alternationMatch) GoString() string { + s := make([]string, len(m)) + for i, m := range m { + s[i] = fmt.Sprintf("%#v", m) + } + return fmt.Sprintf("(%s)", strings.Join(s, " | ")) +} -- cgit v1.2.3-54-g00ecf From 5a4b9f9494bad1091f2f9cb777aed54293b647d3 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 14 Jul 2021 13:21:11 -0700 Subject: time: reference -tags=timetzdata in testing panic This will spare anyone who hits it having to search for the workaround. Change-Id: Iff0d449212f2675ac78e30ae5ffc8efb4d924088 Reviewed-on: https://go-review.googlesource.com/c/go/+/334611 Trust: Josh Bleecher Snyder Run-TryBot: Josh Bleecher Snyder TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/time/internal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time/internal_test.go b/src/time/internal_test.go index 87a4208b05..2c75e449d3 100644 --- a/src/time/internal_test.go +++ b/src/time/internal_test.go @@ -12,7 +12,7 @@ func init() { func initTestingZone() { z, err := loadLocation("America/Los_Angeles", zoneSources[len(zoneSources)-1:]) if err != nil { - panic("cannot load America/Los_Angeles for testing: " + err.Error()) + panic("cannot load America/Los_Angeles for testing: " + err.Error() + "; you may want to use -tags=timetzdata") } z.name = "Local" localLoc = *z -- cgit v1.2.3-54-g00ecf From 1bf2cd1291b5287045d3efd975870897fa03ac1f Mon Sep 17 00:00:00 2001 From: Florin Papa Date: Thu, 6 May 2021 17:17:59 -0700 Subject: debug/elf: retain original error message when getSymbols fails. The original error is currently discarded, and that makes it difficult to know what failed, in case we want to retry only certain errors. Change-Id: Id7e927ec242464249c4dfa5cda0f264adef3c898 Reviewed-on: https://go-review.googlesource.com/c/go/+/317851 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Than McIntosh --- src/debug/elf/file.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index b25d8209e3..e265796ddc 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -494,7 +494,7 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { data, err := symtabSection.Data() if err != nil { - return nil, nil, errors.New("cannot load symbol section") + return nil, nil, fmt.Errorf("cannot load symbol section: %w", err) } symtab := bytes.NewReader(data) if symtab.Len()%Sym32Size != 0 { @@ -503,7 +503,7 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, errors.New("cannot load string table section") + return nil, nil, fmt.Errorf("cannot load string table section: %w", err) } // The first entry is all zeros. @@ -537,7 +537,7 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { data, err := symtabSection.Data() if err != nil { - return nil, nil, errors.New("cannot load symbol section") + return nil, nil, fmt.Errorf("cannot load symbol section: %w", err) } symtab := bytes.NewReader(data) if symtab.Len()%Sym64Size != 0 { @@ -546,7 +546,7 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, errors.New("cannot load string table section") + return nil, nil, fmt.Errorf("cannot load string table section: %w", err) } // The first entry is all zeros. -- cgit v1.2.3-54-g00ecf From cf2fe5d6f12f075f265ba067869fc5f0e3b23ff0 Mon Sep 17 00:00:00 2001 From: WANG Xuerui Date: Mon, 2 Aug 2021 00:56:25 +0800 Subject: doc/asm: fix HTML markup Change-Id: I33bde4835d3b83fafd55beea483f6236c4c62840 Reviewed-on: https://go-review.googlesource.com/c/go/+/338990 Reviewed-by: Ian Lance Taylor Trust: Than McIntosh --- doc/asm.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/asm.html b/doc/asm.html index 51f85eb948..f7787a4076 100644 --- a/doc/asm.html +++ b/doc/asm.html @@ -125,8 +125,8 @@ it is a distinct program, so there are some differences. One is in constant evaluation. Constant expressions in the assembler are parsed using Go's operator precedence, not the C-like precedence of the original. -Thus 3&1<<2 is 4, not 0—it parses as (3&1)<<2 -not 3&(1<<2). +Thus 3&1<<2 is 4, not 0—it parses as (3&1)<<2 +not 3&(1<<2). Also, constants are always evaluated as 64-bit unsigned integers. Thus -2 is not the integer value minus two, but the unsigned 64-bit integer with the same bit pattern. @@ -914,8 +914,6 @@ This assembler is used by GOARCH values ppc64 and ppc64le. Reference: Go PPC64 Assembly Instructions Reference Manual

- -

IBM z/Architecture, a.k.a. s390x

-- cgit v1.2.3-54-g00ecf From a50225a0dc1e83449a76b80b2fbed77af516483c Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Wed, 4 Aug 2021 00:57:07 -0700 Subject: bufio: make Reader.Reset and Writer.Reset work on the zero value For batch allocation reasons, it would be useful to nest a bufio.Reader or bufio.Writer in a struct as a value, rather than a pointer. When the Reset method is called, have it use the default buffer size if the buffer is nil. Fixes #45374 Change-Id: I80df18a13575431428a42ed150a1579de1282637 Reviewed-on: https://go-review.googlesource.com/c/go/+/345570 Trust: Joe Tsai Run-TryBot: Joe Tsai TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/bufio/bufio.go | 10 ++++++++++ src/bufio/bufio_test.go | 25 ++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index ec928e7ad6..391ecf46b3 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -68,7 +68,12 @@ func (b *Reader) Size() int { return len(b.buf) } // Reset discards any buffered data, resets all state, and switches // the buffered reader to read from r. +// Calling Reset on the zero value of Reader initializes the internal buffer +// to the default size. func (b *Reader) Reset(r io.Reader) { + if b.buf == nil { + b.buf = make([]byte, defaultBufSize) + } b.reset(b.buf, r) } @@ -590,7 +595,12 @@ func (b *Writer) Size() int { return len(b.buf) } // Reset discards any unflushed buffered data, clears any error, and // resets b to write its output to w. +// Calling Reset on the zero value of Writer initializes the internal buffer +// to the default size. func (b *Writer) Reset(w io.Writer) { + if b.buf == nil { + b.buf = make([]byte, defaultBufSize) + } b.err = nil b.n = 0 b.wr = w diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index ebcc711db9..eb5136c9ea 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -1312,6 +1312,7 @@ func TestReaderReset(t *testing.T) { if string(buf) != "foo" { t.Errorf("buf = %q; want foo", buf) } + r.Reset(strings.NewReader("bar bar")) all, err := io.ReadAll(r) if err != nil { @@ -1320,12 +1321,23 @@ func TestReaderReset(t *testing.T) { if string(all) != "bar bar" { t.Errorf("ReadAll = %q; want bar bar", all) } + + *r = Reader{} // zero out the Reader + r.Reset(strings.NewReader("bar bar")) + all, err = io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if string(all) != "bar bar" { + t.Errorf("ReadAll = %q; want bar bar", all) + } } func TestWriterReset(t *testing.T) { - var buf1, buf2 bytes.Buffer + var buf1, buf2, buf3 bytes.Buffer w := NewWriter(&buf1) w.WriteString("foo") + w.Reset(&buf2) // and not flushed w.WriteString("bar") w.Flush() @@ -1335,6 +1347,17 @@ func TestWriterReset(t *testing.T) { if buf2.String() != "bar" { t.Errorf("buf2 = %q; want bar", buf2.String()) } + + *w = Writer{} // zero out the Writer + w.Reset(&buf3) // and not flushed + w.WriteString("bar") + w.Flush() + if buf1.String() != "" { + t.Errorf("buf1 = %q; want empty", buf1.String()) + } + if buf3.String() != "bar" { + t.Errorf("buf3 = %q; want bar", buf3.String()) + } } func TestReaderDiscard(t *testing.T) { -- cgit v1.2.3-54-g00ecf From 23832ba2e2fb396cda1dacf3e8afcb38ec36dcba Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Thu, 26 Aug 2021 19:13:22 -0700 Subject: reflect: optimize for maps with string keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Over 80% of all Go map types use a string as the key. The Go runtime already has a specialized implementation for such maps in runtime/map_faststr.go. However, the Go reflection implementation has not historically made use of that implementation. This CL plumbs the appropriate logic to be accessible from Go reflection so that it can benefit as well. name old time/op new time/op delta Map/StringKeys/MapIndex-4 4.65us ± 5% 2.95us ± 3% -36.50% (p=0.016 n=4+5) Map/StringKeys/SetMapIndex-4 7.47us ± 5% 5.27us ± 2% -29.40% (p=0.008 n=5+5) Map/Uint64Keys/MapIndex-4 3.79us ± 3% 3.75us ± 2% ~ (p=0.548 n=5+5) Map/Uint64Keys/SetMapIndex-4 6.13us ± 3% 6.09us ± 1% ~ (p=0.746 n=5+5) Change-Id: I5495d48948d8caf2d004a03ae1820ab5f8729670 Reviewed-on: https://go-review.googlesource.com/c/go/+/345486 Trust: Joe Tsai Run-TryBot: Joe Tsai TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/reflect/all_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/reflect/value.go | 46 ++++++++++++++++++++++++++++++++++++++++------ src/runtime/map.go | 21 +++++++++++++++++++++ 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 293d036f67..e92f71135c 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -7050,6 +7050,53 @@ func BenchmarkNew(b *testing.B) { }) } +func BenchmarkMap(b *testing.B) { + type V *int + value := ValueOf((V)(nil)) + stringKeys := []string{} + mapOfStrings := map[string]V{} + uint64Keys := []uint64{} + mapOfUint64s := map[uint64]V{} + for i := 0; i < 100; i++ { + stringKey := fmt.Sprintf("key%d", i) + stringKeys = append(stringKeys, stringKey) + mapOfStrings[stringKey] = nil + + uint64Key := uint64(i) + uint64Keys = append(uint64Keys, uint64Key) + mapOfUint64s[uint64Key] = nil + } + + tests := []struct { + label string + m, keys, value Value + }{ + {"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value}, + {"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value}, + } + + for _, tt := range tests { + b.Run(tt.label, func(b *testing.B) { + b.Run("MapIndex", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for j := tt.keys.Len() - 1; j >= 0; j-- { + tt.m.MapIndex(tt.keys.Index(j)) + } + } + }) + b.Run("SetMapIndex", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + for j := tt.keys.Len() - 1; j >= 0; j-- { + tt.m.SetMapIndex(tt.keys.Index(j), tt.value) + } + } + }) + }) + } +} + func TestSwapper(t *testing.T) { type I int var a, b, c I diff --git a/src/reflect/value.go b/src/reflect/value.go index bf29d1bb3a..6e9aaabe8a 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1515,15 +1515,21 @@ func (v Value) MapIndex(key Value) Value { // considered unexported. This is consistent with the // behavior for structs, which allow read but not write // of unexported fields. - key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) - var k unsafe.Pointer - if key.flag&flagIndir != 0 { - k = key.ptr + var e unsafe.Pointer + if key.kind() == String && tt.key.Kind() == String { + k := *(*string)(key.ptr) + e = mapaccess_faststr(v.typ, v.pointer(), k) } else { - k = unsafe.Pointer(&key.ptr) + key = key.assignTo("reflect.Value.MapIndex", tt.key, nil) + var k unsafe.Pointer + if key.flag&flagIndir != 0 { + k = key.ptr + } else { + k = unsafe.Pointer(&key.ptr) + } + e = mapaccess(v.typ, v.pointer(), k) } - e := mapaccess(v.typ, v.pointer(), k) if e == nil { return Value{} } @@ -2121,6 +2127,25 @@ func (v Value) SetMapIndex(key, elem Value) { v.mustBeExported() key.mustBeExported() tt := (*mapType)(unsafe.Pointer(v.typ)) + + if key.kind() == String && tt.key.Kind() == String { + k := *(*string)(key.ptr) + if elem.typ == nil { + mapdelete_faststr(v.typ, v.pointer(), k) + return + } + elem.mustBeExported() + elem = elem.assignTo("reflect.Value.SetMapIndex", tt.elem, nil) + var e unsafe.Pointer + if elem.flag&flagIndir != 0 { + e = elem.ptr + } else { + e = unsafe.Pointer(&elem.ptr) + } + mapassign_faststr(v.typ, v.pointer(), k, e) + return + } + key = key.assignTo("reflect.Value.SetMapIndex", tt.key, nil) var k unsafe.Pointer if key.flag&flagIndir != 0 { @@ -3252,12 +3277,21 @@ func makemap(t *rtype, cap int) (m unsafe.Pointer) //go:noescape func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) +//go:noescape +func mapaccess_faststr(t *rtype, m unsafe.Pointer, key string) (val unsafe.Pointer) + //go:noescape func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer) +//go:noescape +func mapassign_faststr(t *rtype, m unsafe.Pointer, key string, val unsafe.Pointer) + //go:noescape func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) +//go:noescape +func mapdelete_faststr(t *rtype, m unsafe.Pointer, key string) + //go:noescape func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter) diff --git a/src/runtime/map.go b/src/runtime/map.go index 59b803d629..985c297cd4 100644 --- a/src/runtime/map.go +++ b/src/runtime/map.go @@ -1324,17 +1324,38 @@ func reflect_mapaccess(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { return elem } +//go:linkname reflect_mapaccess_faststr reflect.mapaccess_faststr +func reflect_mapaccess_faststr(t *maptype, h *hmap, key string) unsafe.Pointer { + elem, ok := mapaccess2_faststr(t, h, key) + if !ok { + // reflect wants nil for a missing element + elem = nil + } + return elem +} + //go:linkname reflect_mapassign reflect.mapassign func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, elem unsafe.Pointer) { p := mapassign(t, h, key) typedmemmove(t.elem, p, elem) } +//go:linkname reflect_mapassign_faststr reflect.mapassign_faststr +func reflect_mapassign_faststr(t *maptype, h *hmap, key string, elem unsafe.Pointer) { + p := mapassign_faststr(t, h, key) + typedmemmove(t.elem, p, elem) +} + //go:linkname reflect_mapdelete reflect.mapdelete func reflect_mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { mapdelete(t, h, key) } +//go:linkname reflect_mapdelete_faststr reflect.mapdelete_faststr +func reflect_mapdelete_faststr(t *maptype, h *hmap, key string) { + mapdelete_faststr(t, h, key) +} + //go:linkname reflect_mapiterinit reflect.mapiterinit func reflect_mapiterinit(t *maptype, h *hmap, it *hiter) { mapiterinit(t, h, it) -- cgit v1.2.3-54-g00ecf From 0d8a4bfc962a606584be0a76ed708f86b44164c7 Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Wed, 4 Aug 2021 01:22:45 -0700 Subject: bufio: add Writer.AvailableBuffer This adds a new Writer.AvailableBuffer method that returns an empty buffer with a possibly non-empty capacity for use with append-like APIs. The typical usage pattern is something like: b := bw.AvailableBuffer() b = appendValue(b, v) bw.Write(b) It allows logic combining append-like APIs with bufio.Writer to avoid needing to allocate and manage buffers themselves and allows the append-like APIs to directly write into the buffer for a bufio.Writer. Fixes #47527 Change-Id: I9cd169f3f8e8c7cd40818caf3daf1944c826fc66 Reviewed-on: https://go-review.googlesource.com/c/go/+/345569 Trust: Joe Tsai Run-TryBot: Joe Tsai TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/bufio/bufio.go | 8 ++++++++ src/bufio/bufio_test.go | 33 +++++++++++++++++++++++++++++++++ src/bufio/example_test.go | 12 ++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index 391ecf46b3..506b84f6ba 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -633,6 +633,14 @@ func (b *Writer) Flush() error { // Available returns how many bytes are unused in the buffer. func (b *Writer) Available() int { return len(b.buf) - b.n } +// AvailableBuffer returns an empty buffer with b.Available() capacity. +// This buffer is intended to be appended to and +// passed to an immediately succeeding Write call. +// The buffer is only valid until the next write operation on b. +func (b *Writer) AvailableBuffer() []byte { + return b.buf[b.n:][:0] +} + // Buffered returns the number of bytes that have been written into the current buffer. func (b *Writer) Buffered() int { return b.n } diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index eb5136c9ea..04a810c206 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -10,6 +10,8 @@ import ( "errors" "fmt" "io" + "math/rand" + "strconv" "strings" "testing" "testing/iotest" @@ -608,6 +610,37 @@ func TestWriter(t *testing.T) { } } +func TestWriterAppend(t *testing.T) { + got := new(bytes.Buffer) + var want []byte + rn := rand.New(rand.NewSource(0)) + w := NewWriterSize(got, 64) + for i := 0; i < 100; i++ { + // Obtain a buffer to append to. + b := w.AvailableBuffer() + if w.Available() != cap(b) { + t.Fatalf("Available() = %v, want %v", w.Available(), cap(b)) + } + + // While not recommended, it is valid to append to a shifted buffer. + // This forces Write to copy the the input. + if rn.Intn(8) == 0 && cap(b) > 0 { + b = b[1:1:cap(b)] + } + + // Append a random integer of varying width. + n := int64(rn.Intn(1 << rn.Intn(30))) + want = append(strconv.AppendInt(want, n, 10), ' ') + b = append(strconv.AppendInt(b, n, 10), ' ') + w.Write(b) + } + w.Flush() + + if !bytes.Equal(got.Bytes(), want) { + t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want) + } +} + // Check that write errors are returned properly. type errorWriterTest struct { diff --git a/src/bufio/example_test.go b/src/bufio/example_test.go index 8885d40549..a864d11012 100644 --- a/src/bufio/example_test.go +++ b/src/bufio/example_test.go @@ -20,6 +20,18 @@ func ExampleWriter() { // Output: Hello, world! } +func ExampleWriter_AvailableBuffer() { + w := bufio.NewWriter(os.Stdout) + for _, i := range []int64{1, 2, 3, 4} { + b := w.AvailableBuffer() + b = strconv.AppendInt(b, i, 10) + b = append(b, ' ') + w.Write(b) + } + w.Flush() + // Output: 1 2 3 4 +} + // The simplest use of a Scanner, to read standard input as a set of lines. func ExampleScanner_lines() { scanner := bufio.NewScanner(os.Stdin) -- cgit v1.2.3-54-g00ecf From ad97d204f02c1f9ad0433e9178d6ce0f3fdb1f9f Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 9 Sep 2021 18:41:30 -0400 Subject: go/types: remove some unnecessary loading/expansion of Named types For Identical an u.nify, only type arguments and pointer identity is needed. Change-Id: Id4018d2a53044fa20fd26d28890f28b37b6d6d70 Reviewed-on: https://go-review.googlesource.com/c/go/+/349409 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/predicates.go | 3 --- src/go/types/unify.go | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 73d240241e..9aa565b68a 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -302,9 +302,6 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - xargs := x.TypeArgs().list() yargs := y.TypeArgs().list() diff --git a/src/go/types/unify.go b/src/go/types/unify.go index ed769aafe8..6d10f71a90 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -425,9 +425,6 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { case *Named: if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - xargs := x.targs.list() yargs := y.targs.list() -- cgit v1.2.3-54-g00ecf From c3b217a0e5f032cadf4595884dad839e169c902c Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Mon, 13 Sep 2021 09:16:39 -0700 Subject: cmd/go: document 'go install cmd@version' ignores vendor directories For #48332 Change-Id: I708eb3e8f3f386f03210b7117d9ab8b0be2125bb Reviewed-on: https://go-review.googlesource.com/c/go/+/349591 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/alldocs.go | 7 +++++-- src/cmd/go/internal/work/build.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 425aa831d8..b7e8212795 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -702,14 +702,17 @@ // // - All arguments must refer to packages in the same module at the same version. // +// - Package path arguments must refer to main packages. Pattern arguments +// will only match main packages. +// // - No module is considered the "main" module. If the module containing // packages named on the command line has a go.mod file, it must not contain // directives (replace and exclude) that would cause it to be interpreted // differently than if it were the main module. The module must not require // a higher version of itself. // -// - Package path arguments must refer to main packages. Pattern arguments -// will only match main packages. +// - Vendor directories are not used in any module. (Vendor directories are not +// included in the module zip files downloaded by 'go install'.) // // If the arguments don't have version suffixes, "go install" may run in // module-aware mode or GOPATH mode, depending on the GO111MODULE environment diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index c51dd398c2..3d7c778a7d 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -496,14 +496,17 @@ allowed, even if they refer to the same version. - All arguments must refer to packages in the same module at the same version. +- Package path arguments must refer to main packages. Pattern arguments +will only match main packages. + - No module is considered the "main" module. If the module containing packages named on the command line has a go.mod file, it must not contain directives (replace and exclude) that would cause it to be interpreted differently than if it were the main module. The module must not require a higher version of itself. -- Package path arguments must refer to main packages. Pattern arguments -will only match main packages. +- Vendor directories are not used in any module. (Vendor directories are not +included in the module zip files downloaded by 'go install'.) If the arguments don't have version suffixes, "go install" may run in module-aware mode or GOPATH mode, depending on the GO111MODULE environment -- cgit v1.2.3-54-g00ecf From bced369a50acf50358f52e5c9c0a30d8bdb707ef Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Thu, 1 Jul 2021 09:27:46 -0400 Subject: cmd/link: minor code cleanup in dwarf gen Minor code cleanup to get rid of a few unused parameters and return values in the linker's dwarf generation code. No functional changes. Change-Id: I1a68ebe0f08d8d32ca7adfdd2fb9db573a4fd5f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/332070 Trust: Than McIntosh Reviewed-by: Cherry Mui Run-TryBot: Than McIntosh TryBot-Result: Go Bot --- src/cmd/link/internal/ld/dwarf.go | 133 ++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 71 deletions(-) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 70138d37ff..839609339f 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -67,20 +67,6 @@ type dwctxt struct { dwmu *sync.Mutex } -func newdwctxt(linkctxt *Link, forTypeGen bool) dwctxt { - d := dwctxt{ - linkctxt: linkctxt, - ldr: linkctxt.loader, - arch: linkctxt.Arch, - tmap: make(map[string]loader.Sym), - tdmap: make(map[loader.Sym]loader.Sym), - rtmap: make(map[loader.Sym]loader.Sym), - } - d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") - d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") - return d -} - // dwSym wraps a loader.Sym; this type is meant to obey the interface // rules for dwarf.Sym from the cmd/internal/dwarf package. DwDie and // DwAttr objects contain references to symbols via this type. @@ -249,7 +235,7 @@ var dwtypes dwarf.DWDie // up all attrs in a single large table, then store indices into the // table in the DIE. This would allow us to common up storage for // attributes that are shared by many DIEs (ex: byte size of N). -func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) *dwarf.DWAttr { +func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface{}) { a := new(dwarf.DWAttr) a.Link = die.Attr die.Attr = a @@ -257,7 +243,6 @@ func newattr(die *dwarf.DWDie, attr uint16, cls int, value int64, data interface a.Cls = uint8(cls) a.Value = value a.Data = data - return a } // Each DIE (except the root ones) has at least 1 attribute: its @@ -290,7 +275,7 @@ func getattr(die *dwarf.DWDie, attr uint16) *dwarf.DWAttr { // The compiler does create nameless DWARF DIEs (ex: concrete subprogram // instance). // FIXME: it would be more efficient to bulk-allocate DIEs. -func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string, version int) *dwarf.DWDie { +func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string) *dwarf.DWDie { die := new(dwarf.DWDie) die.Abbrev = abbrev die.Link = parent.Child @@ -298,10 +283,9 @@ func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string, version in newattr(die, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len(name)), name) - // Sanity check: all DIEs created in the linker should have a non-empty - // name and be version zero. - if name == "" || version != 0 { - panic("nameless or version non-zero DWARF DIE") + // Sanity check: all DIEs created in the linker should be named. + if name == "" { + panic("nameless DWARF DIE") } var st sym.SymKind @@ -321,7 +305,7 @@ func (d *dwctxt) newdie(parent *dwarf.DWDie, abbrev int, name string, version in // this also includes loose ends such as STRUCT_FIELD. st = sym.SDWARFTYPE } - ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, version) + ds := d.ldr.LookupOrCreateSym(dwarf.InfoPrefix+name, 0) dsu := d.ldr.MakeSymbolUpdater(ds) dsu.SetType(st) d.ldr.SetAttrNotInSymbolTable(ds, true) @@ -397,22 +381,20 @@ func (d *dwctxt) mustFind(name string) loader.Sym { return r } -func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) int64 { - var result int64 +func (d *dwctxt) adddwarfref(sb *loader.SymbolBuilder, t loader.Sym, size int) { switch size { default: d.linkctxt.Errorf(sb.Sym(), "invalid size %d in adddwarfref\n", size) case d.arch.PtrSize, 4: } - result = sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) - return result + sb.AddSymRef(d.arch, t, 0, objabi.R_DWARFSECREF, size) } -func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) *dwarf.DWAttr { +func (d *dwctxt) newrefattr(die *dwarf.DWDie, attr uint16, ref loader.Sym) { if ref == 0 { - return nil + return } - return newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) + newattr(die, attr, dwarf.DW_CLS_REFERENCE, 0, dwSym(ref)) } func (d *dwctxt) dtolsym(s dwarf.Sym) loader.Sym { @@ -481,7 +463,7 @@ func (d *dwctxt) lookupOrDiag(n string) loader.Sym { return symIdx } -func (d *dwctxt) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, def *dwarf.DWDie) *dwarf.DWDie { +func (d *dwctxt) dotypedef(parent *dwarf.DWDie, name string, def *dwarf.DWDie) *dwarf.DWDie { // Only emit typedefs for real names. if strings.HasPrefix(name, "map[") { return nil @@ -513,7 +495,7 @@ func (d *dwctxt) dotypedef(parent *dwarf.DWDie, gotype loader.Sym, name string, // so that future lookups will find the typedef instead // of the real definition. This hooks the typedef into any // circular definition loops, so that gdb can understand them. - die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name, 0) + die := d.newdie(parent, dwarf.DW_ABRV_TYPEDECL, name) d.newrefattr(die, dwarf.DW_AT_type, tds) @@ -558,7 +540,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { var die, typedefdie *dwarf.DWDie switch kind { case objabi.KindBool: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_boolean, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -567,7 +549,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { objabi.KindInt16, objabi.KindInt32, objabi.KindInt64: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_signed, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) @@ -577,29 +559,29 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { objabi.KindUint32, objabi.KindUint64, objabi.KindUintptr: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_unsigned, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindFloat32, objabi.KindFloat64: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindComplex64, objabi.KindComplex128: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BASETYPE, name) newattr(die, dwarf.DW_AT_encoding, dwarf.DW_CLS_CONSTANT, dwarf.DW_ATE_complex_float, 0) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindArray: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_ARRAYTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) s := decodetypeArrayElem(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) - fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range", 0) + fld := d.newdie(die, dwarf.DW_ABRV_ARRAYRANGE, "range") // use actual length not upper bound; correct for 0-length arrays. newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, decodetypeArrayLen(d.ldr, d.arch, gotype), 0) @@ -607,7 +589,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) case objabi.KindChan: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_CHANTYPE, name) s := decodetypeChanElem(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_go_elem, d.defgotype(s)) // Save elem type for synthesizechantypes. We could synthesize here @@ -615,9 +597,9 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { d.newrefattr(die, dwarf.DW_AT_type, s) case objabi.KindFunc: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_FUNCTYPE, name) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + typedefdie = d.dotypedef(&dwtypes, name, die) data := d.ldr.Data(gotype) // FIXME: add caching or reuse reloc slice. relocs := d.ldr.Relocs(gotype) @@ -625,24 +607,24 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { for i := 0; i < nfields; i++ { s := decodetypeFuncInType(d.ldr, d.arch, gotype, &relocs, i) sn := d.ldr.SymName(s) - fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) } if decodetypeFuncDotdotdot(d.arch, data) { - d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...", 0) + d.newdie(die, dwarf.DW_ABRV_DOTDOTDOT, "...") } nfields = decodetypeFuncOutCount(d.arch, data) for i := 0; i < nfields; i++ { s := decodetypeFuncOutType(d.ldr, d.arch, gotype, &relocs, i) sn := d.ldr.SymName(s) - fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:], 0) + fld := d.newdie(die, dwarf.DW_ABRV_FUNCTYPEPARAM, sn[5:]) d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.defgotype(s))) } case objabi.KindInterface: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_IFACETYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) data := d.ldr.Data(gotype) nfields := int(decodetypeIfaceMethodCount(d.arch, data)) var s loader.Sym @@ -654,7 +636,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindMap: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_MAPTYPE, name) s := decodetypeMapKey(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_go_key, d.defgotype(s)) s = decodetypeMapValue(d.ldr, d.arch, gotype) @@ -664,26 +646,26 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { d.newrefattr(die, dwarf.DW_AT_type, gotype) case objabi.KindPtr: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) s := decodetypePtrElem(d.ldr, d.arch, gotype) d.newrefattr(die, dwarf.DW_AT_type, d.defgotype(s)) case objabi.KindSlice: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_SLICETYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) s := decodetypeArrayElem(d.ldr, d.arch, gotype) elem := d.defgotype(s) d.newrefattr(die, dwarf.DW_AT_go_elem, elem) case objabi.KindString: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRINGTYPE, name) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) case objabi.KindStruct: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name, 0) - typedefdie = d.dotypedef(&dwtypes, gotype, name, die) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_STRUCTTYPE, name) + typedefdie = d.dotypedef(&dwtypes, name, die) newattr(die, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, bytesize, 0) nfields := decodetypeStructFieldCount(d.ldr, d.arch, gotype) for i := 0; i < nfields; i++ { @@ -693,7 +675,7 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { sn := d.ldr.SymName(s) f = sn[5:] // skip "type." } - fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f, 0) + fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f) d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s)) offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i) newmemberoffsetattr(fld, int32(offsetAnon>>1)) @@ -703,11 +685,11 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { } case objabi.KindUnsafePointer: - die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_BARE_PTRTYPE, name) default: d.linkctxt.Errorf(gotype, "dwarf: definition of unknown kind %d", kind) - die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name, 0) + die = d.newdie(&dwtypes, dwarf.DW_ABRV_TYPEDECL, name) d.newrefattr(die, dwarf.DW_AT_type, d.mustFind("")) } @@ -754,7 +736,7 @@ func (d *dwctxt) defptrto(dwtype loader.Sym) loader.Sym { return die } - pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname, 0) + pdie := d.newdie(&dwtypes, dwarf.DW_ABRV_PTRTYPE, ptrname) d.newrefattr(pdie, dwarf.DW_AT_type, dwtype) // The DWARF info synthesizes pointer types that don't exist at the @@ -782,7 +764,7 @@ func (d *dwctxt) copychildrenexcept(ctxt *Link, dst *dwarf.DWDie, src *dwarf.DWD if src == except { continue } - c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string), 0) + c := d.newdie(dst, src.Abbrev, getattr(src, dwarf.DW_AT_name).Data.(string)) for a := src.Attr; a != nil; a = a.Link { newattr(c, a.Atr, int(a.Cls), a.Value, a.Data) } @@ -877,7 +859,7 @@ func (d *dwctxt) mkinternaltype(ctxt *Link, abbrev int, typename, keyname, valna if s != 0 && d.ldr.SymType(s) == sym.SDWARFTYPE { return s } - die := d.newdie(&dwtypes, abbrev, name, 0) + die := d.newdie(&dwtypes, abbrev, name) f(die) return d.dtolsym(die.Sym) } @@ -922,7 +904,7 @@ func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { t = d.defptrto(keytype) } d.newrefattr(dwhk, dwarf.DW_AT_type, t) - fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size") newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) @@ -936,7 +918,7 @@ func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { t = d.defptrto(valtype) } d.newrefattr(dwhv, dwarf.DW_AT_type, t) - fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size", 0) + fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size") newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, BucketSize, 0) d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) }) @@ -947,17 +929,17 @@ func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) { // bucket. "data" will be replaced with keys/values below. d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data")) - fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys", 0) + fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys") d.newrefattr(fld, dwarf.DW_AT_type, dwhks) newmemberoffsetattr(fld, BucketSize) - fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values", 0) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values") d.newrefattr(fld, dwarf.DW_AT_type, dwhvs) newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) - fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow", 0) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow") d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym))) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) if d.arch.RegSize > d.arch.PtrSize { - fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad", 0) + fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad") d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize)) } @@ -1672,7 +1654,7 @@ func dwarfEnabled(ctxt *Link) bool { // newly created builtin type DIE 'typeDie'. func (d *dwctxt) mkBuiltinType(ctxt *Link, abrv int, tname string) *dwarf.DWDie { // create type DIE - die := d.newdie(&dwtypes, abrv, tname, 0) + die := d.newdie(&dwtypes, abrv, tname) // Look up type symbol. gotype := d.lookupOrDiag("type." + tname) @@ -1765,7 +1747,16 @@ func dwarfGenerateDebugInfo(ctxt *Link) { return } - d := newdwctxt(ctxt, true) + d := &dwctxt{ + linkctxt: ctxt, + ldr: ctxt.loader, + arch: ctxt.Arch, + tmap: make(map[string]loader.Sym), + tdmap: make(map[loader.Sym]loader.Sym), + rtmap: make(map[loader.Sym]loader.Sym), + } + d.typeRuntimeEface = d.lookupOrDiag("type.runtime.eface") + d.typeRuntimeIface = d.lookupOrDiag("type.runtime.iface") if ctxt.HeadType == objabi.Haix { // Initial map used to store package size for each DWARF section. @@ -1776,7 +1767,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) { newattr(&dwtypes, dwarf.DW_AT_name, dwarf.DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") // Unspecified type. There are no references to this in the symbol table. - d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "", 0) + d.newdie(&dwtypes, dwarf.DW_ABRV_NULLTYPE, "") // Some types that must exist to define other ones (uintptr in particular // is needed for array size) @@ -1841,7 +1832,7 @@ func dwarfGenerateDebugInfo(ctxt *Link) { if len(unit.Textp) == 0 { cuabrv = dwarf.DW_ABRV_COMPUNIT_TEXTLESS } - unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg, 0) + unit.DWInfo = d.newdie(&dwroot, cuabrv, unit.Lib.Pkg) newattr(unit.DWInfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(dwarf.DW_LANG_Go), 0) // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. compDir := getCompilationDir() -- cgit v1.2.3-54-g00ecf From e74e363a6b3e71ec5a49a3aae8c2523abb72faa7 Mon Sep 17 00:00:00 2001 From: Martin Möhrmann Date: Sat, 28 Aug 2021 17:54:10 +0200 Subject: strings: add Clone function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new strings.Clone function copies the input string without the returned cloned string referencing the input strings memory. goarch: amd64 cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz name time/op Clone-8 24.2ns ± 2% name alloc/op Clone-8 48.0B ± 0% name allocs/op Clone-8 1.00 ± 0% Update #45038 Fixes #40200 Change-Id: Id9116c21c14328ec3931ef9a67a2e4f30ff301f9 Reviewed-on: https://go-review.googlesource.com/c/go/+/345849 Trust: Martin Möhrmann Run-TryBot: Martin Möhrmann TryBot-Result: Go Bot Reviewed-by: Joe Tsai Reviewed-by: Ian Lance Taylor --- src/strings/clone.go | 23 +++++++++++++++++++++++ src/strings/clone_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/strings/clone.go create mode 100644 src/strings/clone_test.go diff --git a/src/strings/clone.go b/src/strings/clone.go new file mode 100644 index 0000000000..6097c6cc88 --- /dev/null +++ b/src/strings/clone.go @@ -0,0 +1,23 @@ +// Copyright 2021 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 strings + +import ( + "unsafe" +) + +// Clone returns a fresh copy of s. +// It guarantees to make a copy of s into a new allocation, +// which can be important when retaining only a small substring +// of a much larger string. Using Clone can help such programs +// use less memory. Of course, since using Clone makes a copy, +// overuse of Clone can make programs use more memory. +// Clone should typically be used only rarely, and only when +// profiling indicates that it is needed. +func Clone(s string) string { + b := make([]byte, len(s)) + copy(b, s) + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/src/strings/clone_test.go b/src/strings/clone_test.go new file mode 100644 index 0000000000..5396771047 --- /dev/null +++ b/src/strings/clone_test.go @@ -0,0 +1,40 @@ +// Copyright 2021 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 strings_test + +import ( + "reflect" + "strings" + "testing" + "unsafe" +) + +func TestClone(t *testing.T) { + var cloneTests = []string{ + "", + "short", + strings.Repeat("a", 42), + } + for _, input := range cloneTests { + clone := strings.Clone(input) + if clone != input { + t.Errorf("Clone(%q) = %q; want %q", input, clone, input) + } + + inputHeader := (*reflect.StringHeader)(unsafe.Pointer(&input)) + cloneHeader := (*reflect.StringHeader)(unsafe.Pointer(&clone)) + if inputHeader.Data == cloneHeader.Data { + t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input) + } + } +} + +func BenchmarkClone(b *testing.B) { + var str = strings.Repeat("a", 42) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + stringSink = strings.Clone(str) + } +} -- cgit v1.2.3-54-g00ecf From c8a58f29dcb2b4f38ca4fcf4d2a2a80f606c9573 Mon Sep 17 00:00:00 2001 From: Michael Matloob Date: Wed, 8 Sep 2021 17:28:09 -0400 Subject: cmd/go: add test to check for a potential workspace loading issue This test checks that we load the same graph regardless of the path to the requested module in the workspace. We currently don't. This will be fixed in a future change that redoes workspace mode's usage of the Requirements structure. For #45713 Change-Id: Id02cbb60a38619d840dbf1e70173ce853c0c167a Reviewed-on: https://go-review.googlesource.com/c/go/+/348649 Trust: Michael Matloob Run-TryBot: Michael Matloob TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/testdata/script/work_prune.txt | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/cmd/go/testdata/script/work_prune.txt diff --git a/src/cmd/go/testdata/script/work_prune.txt b/src/cmd/go/testdata/script/work_prune.txt new file mode 100644 index 0000000000..7347b312ee --- /dev/null +++ b/src/cmd/go/testdata/script/work_prune.txt @@ -0,0 +1,104 @@ +# This test makes sure workspace mode's handling of the module graph +# is compatible with module pruning. The graph we load from either of +# the workspace modules should be the same, even if their graphs +# don't overlap. +# +# This is the module graph in the test: +# +# example.com/a -> example.com/b v1.0.0 -> example.com/q v1.1.0 +# example.com/p -> example.com/q v1.0.0 +# +# If we didn't load the whole graph and didn't load the dependencies of b +# when loading p, we would end up loading q v1.0.0, rather than v1.1.0, +# which is selected by MVS. +# TODO(#48331): We currently load the wrong version of q. Fix this. + +go list -m -f '{{.Version}}' example.com/q +stdout '^v1.0.0$' # TODO(#48331): This should be 1.1.0. Fix this. + +-- go.work -- +go 1.18 + +directory ( + ./a + ./p +) +-- a/go.mod -- +module example.com/a + +go 1.18 + +require example.com/b v1.0.0 + +replace example.com/b v1.0.0 => ../b +-- a/foo.go -- +package main + +import "example.com/b" + +func main() { + b.B() +} +-- b/go.mod -- +module example.com/b + +go 1.18 + +require example.com/q v1.1.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- b/b.go -- +package b + +func B() { +} +-- b/b_test.go -- +package b + +import "example.com/q" + +func TestB() { + q.PrintVersion +} +-- p/go.mod -- +module example.com/p + +go 1.18 + +require example.com/q v1.0.0 + +replace example.com/q v1.0.0 => ../q1_0_0 +replace example.com/q v1.1.0 => ../q1_1_0 +-- p/main.go -- +package main + +import "example.com/q" + +func main() { + q.PrintVersion() +} +-- q1_0_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_0_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.0.0") +} +-- q1_1_0/go.mod -- +module example.com/q + +go 1.18 +-- q1_1_0/q.go -- +package q + +import "fmt" + +func PrintVersion() { + fmt.Println("version 1.1.0") +} -- cgit v1.2.3-54-g00ecf From ac40c9872f6e8ef095dcc6ee556236782eee4f76 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Mon, 13 Sep 2021 09:17:22 -0700 Subject: reflect: fix _faststr optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CL 345486 introduced an optimization to reflect's map accesses which is not quite correct. We can't use the optimized code if the value type is >128 bytes. See cmd/compile/internal/walk/walk.go:mapfast Fixes #48357 Change-Id: I8e3c7858693083dd4393a8de48ca5fa47bab66f2 Reviewed-on: https://go-review.googlesource.com/c/go/+/349593 Trust: Keith Randall Trust: Joe Tsai Trust: Josh Bleecher Snyder Trust: Martin Möhrmann Run-TryBot: Keith Randall Run-TryBot: Joe Tsai Reviewed-by: Joe Tsai Reviewed-by: Josh Bleecher Snyder Reviewed-by: Martin Möhrmann TryBot-Result: Go Bot --- src/reflect/value.go | 4 ++-- test/fixedbugs/issue48357.go | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test/fixedbugs/issue48357.go diff --git a/src/reflect/value.go b/src/reflect/value.go index 6e9aaabe8a..bc48a76ce6 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1517,7 +1517,7 @@ func (v Value) MapIndex(key Value) Value { // of unexported fields. var e unsafe.Pointer - if key.kind() == String && tt.key.Kind() == String { + if key.kind() == String && tt.key.Kind() == String && tt.elem.size <= maxValSize { k := *(*string)(key.ptr) e = mapaccess_faststr(v.typ, v.pointer(), k) } else { @@ -2128,7 +2128,7 @@ func (v Value) SetMapIndex(key, elem Value) { key.mustBeExported() tt := (*mapType)(unsafe.Pointer(v.typ)) - if key.kind() == String && tt.key.Kind() == String { + if key.kind() == String && tt.key.Kind() == String && tt.elem.size <= maxValSize { k := *(*string)(key.ptr) if elem.typ == nil { mapdelete_faststr(v.typ, v.pointer(), k) diff --git a/test/fixedbugs/issue48357.go b/test/fixedbugs/issue48357.go new file mode 100644 index 0000000000..5b39fc43d4 --- /dev/null +++ b/test/fixedbugs/issue48357.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2021 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 main + +import "reflect" + +type T [129]byte + +func main() { + m := map[string]T{} + v := reflect.ValueOf(m) + v.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(T{})) + g = m["a"] +} + +var g T -- cgit v1.2.3-54-g00ecf From a0c409cbc82ea9999a03fa0bfe6d9a8953e780e0 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 7 May 2021 18:39:36 -0700 Subject: reflect: add fast paths for common, simple Kinds to DeepEqual MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Though the normal equality check suffices, it allocates. Handle common, simple kinds without allocating. For some real world types compared with DeepEqual in the Tailscale code base, the impact of these changes: name old time/op new time/op delta HostInfoEqual-8 25.9µs ± 0% 14.4µs ± 0% -44.32% (p=0.008 n=5+5) name old alloc/op new alloc/op delta HostInfoEqual-8 18.8kB ± 0% 12.6kB ± 0% -32.58% (p=0.008 n=5+5) name old allocs/op new allocs/op delta HostInfoEqual-8 548 ± 0% 90 ± 0% -83.58% (p=0.008 n=5+5) For the benchmarks added in this commit: name old time/op new time/op delta DeepEqual/int8-8 40.1ns ± 4% 31.3ns ± 4% -21.79% (p=0.000 n=19+20) DeepEqual/[]int8-8 169ns ± 1% 123ns ± 1% -27.05% (p=0.000 n=17+18) DeepEqual/int16-8 39.9ns ± 2% 30.8ns ± 4% -22.81% (p=0.000 n=17+20) DeepEqual/[]int16-8 172ns ± 0% 122ns ± 1% -28.91% (p=0.000 n=17+18) DeepEqual/int32-8 40.4ns ± 3% 31.1ns ± 4% -23.07% (p=0.000 n=20+20) DeepEqual/[]int32-8 180ns ± 1% 124ns ± 1% -31.06% (p=0.000 n=20+18) DeepEqual/int64-8 40.1ns ± 2% 31.4ns ± 4% -21.82% (p=0.000 n=20+20) DeepEqual/[]int64-8 180ns ± 1% 124ns ± 1% -31.47% (p=0.000 n=16+19) DeepEqual/int-8 40.1ns ± 4% 30.9ns ± 3% -22.88% (p=0.000 n=20+18) DeepEqual/[]int-8 180ns ± 0% 123ns ± 2% -31.59% (p=0.000 n=19+20) DeepEqual/uint8-8 39.8ns ± 3% 31.9ns ± 5% -19.72% (p=0.000 n=20+20) DeepEqual/[]uint8-8 168ns ± 1% 114ns ± 1% -32.48% (p=0.000 n=18+19) DeepEqual/uint16-8 40.3ns ± 4% 31.4ns ± 6% -22.14% (p=0.000 n=20+20) DeepEqual/[]uint16-8 173ns ± 1% 124ns ± 1% -28.20% (p=0.000 n=20+16) DeepEqual/uint32-8 40.1ns ± 3% 30.7ns ± 3% -23.48% (p=0.000 n=20+20) DeepEqual/[]uint32-8 180ns ± 1% 123ns ± 1% -31.56% (p=0.000 n=20+18) DeepEqual/uint64-8 40.0ns ± 4% 31.3ns ± 4% -21.80% (p=0.000 n=19+19) DeepEqual/[]uint64-8 180ns ± 1% 124ns ± 0% -31.45% (p=0.000 n=18+18) DeepEqual/uint-8 39.8ns ± 3% 31.1ns ± 4% -21.95% (p=0.000 n=19+20) DeepEqual/[]uint-8 181ns ± 1% 122ns ± 1% -32.33% (p=0.000 n=17+20) DeepEqual/uintptr-8 40.3ns ± 3% 31.2ns ± 3% -22.66% (p=0.000 n=20+18) DeepEqual/[]uintptr-8 181ns ± 1% 124ns ± 1% -31.46% (p=0.000 n=19+16) DeepEqual/float32-8 40.3ns ± 2% 31.2ns ± 3% -22.52% (p=0.000 n=16+20) DeepEqual/[]float32-8 180ns ± 1% 122ns ± 1% -32.18% (p=0.000 n=17+17) DeepEqual/float64-8 40.6ns ± 3% 30.9ns ± 5% -23.91% (p=0.000 n=19+20) DeepEqual/[]float64-8 182ns ± 2% 121ns ± 1% -33.33% (p=0.000 n=18+20) DeepEqual/complex64-8 43.0ns ±11% 32.1ns ± 5% -25.33% (p=0.000 n=20+18) DeepEqual/[]complex64-8 182ns ± 1% 122ns ± 2% -32.60% (p=0.000 n=18+19) DeepEqual/complex128-8 42.4ns ± 4% 34.2ns ± 3% -19.35% (p=0.000 n=20+19) DeepEqual/[]complex128-8 197ns ± 1% 122ns ± 1% -38.01% (p=0.000 n=19+19) DeepEqual/bool-8 40.3ns ± 3% 32.9ns ± 5% -18.33% (p=0.000 n=20+20) DeepEqual/[]bool-8 169ns ± 1% 124ns ± 1% -26.90% (p=0.000 n=18+19) DeepEqual/string-8 41.4ns ± 3% 33.7ns ± 5% -18.50% (p=0.000 n=19+20) DeepEqual/[]string-8 216ns ± 0% 128ns ± 1% -41.05% (p=0.000 n=19+17) DeepEqual/[]uint8#01-8 507ns ± 1% 112ns ± 2% -77.92% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 613ns ± 1% 210ns ± 1% -65.76% (p=0.000 n=18+19) DeepEqual/[6]uint8-8 228ns ± 1% 162ns ± 1% -29.00% (p=0.000 n=20+19) DeepEqual/[][6]uint8-8 546ns ± 2% 269ns ± 1% -50.72% (p=0.000 n=20+19) name old alloc/op new alloc/op delta DeepEqual/int8-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int8-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int16-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int16-8 4.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/int-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uint8-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint8-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint16-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint16-8 4.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uint-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uintptr-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uintptr-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/float32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]float32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/float64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]float64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/complex64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]complex64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/complex128-8 0.00B 0.00B ~ (all equal) DeepEqual/[]complex128-8 32.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/bool-8 0.00B 0.00B ~ (all equal) DeepEqual/[]bool-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/string-8 0.00B 0.00B ~ (all equal) DeepEqual/[]string-8 32.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[]uint8#01-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[6]uint8-8 0.00B 0.00B ~ (all equal) DeepEqual/[][6]uint8-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) name old allocs/op new allocs/op delta DeepEqual/int8-8 0.00 0.00 ~ (all equal) DeepEqual/[]int8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int16-8 0.00 0.00 ~ (all equal) DeepEqual/[]int16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int32-8 0.00 0.00 ~ (all equal) DeepEqual/[]int32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int64-8 0.00 0.00 ~ (all equal) DeepEqual/[]int64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int-8 0.00 0.00 ~ (all equal) DeepEqual/[]int-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint8-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint16-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint32-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint64-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uintptr-8 0.00 0.00 ~ (all equal) DeepEqual/[]uintptr-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/float32-8 0.00 0.00 ~ (all equal) DeepEqual/[]float32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/float64-8 0.00 0.00 ~ (all equal) DeepEqual/[]float64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/complex64-8 0.00 0.00 ~ (all equal) DeepEqual/[]complex64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/complex128-8 0.00 0.00 ~ (all equal) DeepEqual/[]complex128-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/bool-8 0.00 0.00 ~ (all equal) DeepEqual/[]bool-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/string-8 0.00 0.00 ~ (all equal) DeepEqual/[]string-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/[]uint8#01-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) DeepEqual/[6]uint8-8 0.00 0.00 ~ (all equal) DeepEqual/[][6]uint8-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) Change-Id: Ic21f0e2305f2cf5e6674c81b9ca609120b3006d9 Reviewed-on: https://go-review.googlesource.com/c/go/+/318169 Trust: Josh Bleecher Snyder Trust: Joe Tsai Run-TryBot: Josh Bleecher Snyder Run-TryBot: Joe Tsai TryBot-Result: Go Bot Reviewed-by: Joe Tsai --- src/reflect/all_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ src/reflect/deepequal.go | 21 ++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index e92f71135c..5b147082bb 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -905,6 +905,9 @@ var deepEqualTests = []DeepEqualTest{ {error(nil), error(nil), true}, {map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true}, {fn1, fn2, true}, + {[]byte{1, 2, 3}, []byte{1, 2, 3}, true}, + {[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true}, + {MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true}, // Inequalities {1, 2, false}, @@ -950,6 +953,9 @@ var deepEqualTests = []DeepEqualTest{ {&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false}, {Basic{1, 0.5}, NotBasic{1, 0.5}, false}, {map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false}, + {[]byte{1, 2, 3}, []MyByte{1, 2, 3}, false}, + {[]MyByte{1, 2, 3}, MyBytes{1, 2, 3}, false}, + {[]byte{1, 2, 3}, MyBytes{1, 2, 3}, false}, // Possible loops. {&loop1, &loop1, true}, @@ -1049,6 +1055,82 @@ func TestDeepEqualUnexportedMap(t *testing.T) { } } +var deepEqualPerfTests = []struct { + x, y interface{} +}{ + {x: int8(99), y: int8(99)}, + {x: []int8{99}, y: []int8{99}}, + {x: int16(99), y: int16(99)}, + {x: []int16{99}, y: []int16{99}}, + {x: int32(99), y: int32(99)}, + {x: []int32{99}, y: []int32{99}}, + {x: int64(99), y: int64(99)}, + {x: []int64{99}, y: []int64{99}}, + {x: int(999999), y: int(999999)}, + {x: []int{999999}, y: []int{999999}}, + + {x: uint8(99), y: uint8(99)}, + {x: []uint8{99}, y: []uint8{99}}, + {x: uint16(99), y: uint16(99)}, + {x: []uint16{99}, y: []uint16{99}}, + {x: uint32(99), y: uint32(99)}, + {x: []uint32{99}, y: []uint32{99}}, + {x: uint64(99), y: uint64(99)}, + {x: []uint64{99}, y: []uint64{99}}, + {x: uint(999999), y: uint(999999)}, + {x: []uint{999999}, y: []uint{999999}}, + {x: uintptr(999999), y: uintptr(999999)}, + {x: []uintptr{999999}, y: []uintptr{999999}}, + + {x: float32(1.414), y: float32(1.414)}, + {x: []float32{1.414}, y: []float32{1.414}}, + {x: float64(1.414), y: float64(1.414)}, + {x: []float64{1.414}, y: []float64{1.414}}, + + {x: complex64(1.414), y: complex64(1.414)}, + {x: []complex64{1.414}, y: []complex64{1.414}}, + {x: complex128(1.414), y: complex128(1.414)}, + {x: []complex128{1.414}, y: []complex128{1.414}}, + + {x: true, y: true}, + {x: []bool{true}, y: []bool{true}}, + + {x: "abcdef", y: "abcdef"}, + {x: []string{"abcdef"}, y: []string{"abcdef"}}, + + {x: []byte("abcdef"), y: []byte("abcdef")}, + {x: [][]byte{[]byte("abcdef")}, y: [][]byte{[]byte("abcdef")}}, + + {x: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}, y: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, + {x: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, y: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}}, +} + +func TestDeepEqualAllocs(t *testing.T) { + for _, tt := range deepEqualPerfTests { + t.Run(ValueOf(tt.x).Type().String(), func(t *testing.T) { + got := testing.AllocsPerRun(100, func() { + if !DeepEqual(tt.x, tt.y) { + t.Errorf("DeepEqual(%v, %v)=false", tt.x, tt.y) + } + }) + if int(got) != 0 { + t.Errorf("DeepEqual(%v, %v) allocated %d times", tt.x, tt.y, int(got)) + } + }) + } +} + +func BenchmarkDeepEqual(b *testing.B) { + for _, bb := range deepEqualPerfTests { + b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sink = DeepEqual(bb.x, bb.y) + } + }) + } +} + func check2ndField(x interface{}, offs uintptr, t *testing.T) { s := ValueOf(x) f := s.Type().Field(1) diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go index d951d8d999..94174dec04 100644 --- a/src/reflect/deepequal.go +++ b/src/reflect/deepequal.go @@ -6,7 +6,10 @@ package reflect -import "unsafe" +import ( + "internal/bytealg" + "unsafe" +) // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all @@ -102,6 +105,10 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { if v1.Pointer() == v2.Pointer() { return true } + // Special case for []byte, which is common. + if v1.Type().Elem().Kind() == Uint8 { + return bytealg.Equal(v1.Bytes(), v2.Bytes()) + } for i := 0; i < v1.Len(); i++ { if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { return false @@ -149,6 +156,18 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { } // Can't do better than this: return false + case Int, Int8, Int16, Int32, Int64: + return v1.Int() == v2.Int() + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return v1.Uint() == v2.Uint() + case String: + return v1.String() == v2.String() + case Bool: + return v1.Bool() == v2.Bool() + case Float32, Float64: + return v1.Float() == v2.Float() + case Complex64, Complex128: + return v1.Complex() == v2.Complex() default: // Normal equality suffices return valueInterface(v1, false) == valueInterface(v2, false) -- cgit v1.2.3-54-g00ecf From f93a63addbbca69d7817c8993a88511ec31424cd Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 13 Sep 2021 12:49:08 -0700 Subject: reflect: add a floating point section to DeepEqual tests The floating point tests were all added into the Inequalities section, instead of separated into Equalities vs Inequalities. Rather than separate them, add a new floating point section. Change-Id: I3713a5aff5850dcc0caf68a754633d695a03ded9 Reviewed-on: https://go-review.googlesource.com/c/go/+/349612 Trust: Josh Bleecher Snyder Trust: Joe Tsai Run-TryBot: Josh Bleecher Snyder Run-TryBot: Joe Tsai Reviewed-by: Joe Tsai TryBot-Result: Go Bot --- src/reflect/all_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 5b147082bb..22885c548f 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -928,6 +928,9 @@ var deepEqualTests = []DeepEqualTest{ {fn1, fn3, false}, {fn3, fn3, false}, {[][]int{{1}}, [][]int{{2}}, false}, + {&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false}, + + // Fun with floating point. {math.NaN(), math.NaN(), false}, {&[1]float64{math.NaN()}, &[1]float64{math.NaN()}, false}, {&[1]float64{math.NaN()}, self{}, true}, @@ -935,7 +938,6 @@ var deepEqualTests = []DeepEqualTest{ {[]float64{math.NaN()}, self{}, true}, {map[float64]float64{math.NaN(): 1}, map[float64]float64{1: 2}, false}, {map[float64]float64{math.NaN(): 1}, self{}, true}, - {&structWithSelfPtr{p: &structWithSelfPtr{s: "a"}}, &structWithSelfPtr{p: &structWithSelfPtr{s: "b"}}, false}, // Nil vs empty: not the same. {[]int{}, []int(nil), false}, -- cgit v1.2.3-54-g00ecf From 81a4fe6fd29a427c613038260ea12c5374cc5894 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 13 Sep 2021 17:26:03 +0200 Subject: cmd/link/internal/ld: re-enable DWARF tests on solaris/illumos It looks like these are fixed on current tip after CL 84655 marked them to be skipped. Fixes #23168 Change-Id: I0020e6da1042f723eb54186ef0fe925df5326230 Reviewed-on: https://go-review.googlesource.com/c/go/+/349250 Trust: Tobias Klauser Run-TryBot: Tobias Klauser Reviewed-by: Cherry Mui Reviewed-by: Than McIntosh TryBot-Result: Go Bot --- src/cmd/link/internal/ld/dwarf_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 543dd5caac..0aeaa7565c 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -614,9 +614,6 @@ func TestInlinedRoutineRecords(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } - if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { - t.Skip("skipping on solaris, illumos, pending resolution of issue #23168") - } t.Parallel() @@ -851,9 +848,6 @@ func TestAbstractOriginSanity(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } - if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { - t.Skip("skipping on solaris, illumos, pending resolution of issue #23168") - } if wd, err := os.Getwd(); err == nil { gopathdir := filepath.Join(wd, "testdata", "httptest") @@ -869,9 +863,6 @@ func TestAbstractOriginSanityIssue25459(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } - if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { - t.Skip("skipping on solaris, illumos, pending resolution of issue #23168") - } if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" { t.Skip("skipping on not-amd64 not-386; location lists not supported") } @@ -890,9 +881,6 @@ func TestAbstractOriginSanityIssue26237(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; no DWARF symbol table in executables") } - if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { - t.Skip("skipping on solaris, illumos, pending resolution of issue #23168") - } if wd, err := os.Getwd(); err == nil { gopathdir := filepath.Join(wd, "testdata", "issue26237") abstractOriginSanity(t, gopathdir, DefaultOpt) -- cgit v1.2.3-54-g00ecf From 960d036f8f7387de9b06fde6601af43ecaa650e6 Mon Sep 17 00:00:00 2001 From: Hossein Zolfi Date: Mon, 13 Sep 2021 19:16:28 +0000 Subject: cmd/go: add missing parenthesis in a call to "PrintVersion" For #45713 Change-Id: I16e548e6c10e58da815d08897f4ba5d71eeb17e4 GitHub-Last-Rev: 4a0c5d0cdaba94e5950effdcb0ef6b736c9556d1 GitHub-Pull-Request: golang/go#48360 Reviewed-on: https://go-review.googlesource.com/c/go/+/349599 Reviewed-by: Bryan C. Mills Trust: Bryan C. Mills Trust: Jay Conrod Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot --- src/cmd/go/testdata/script/work_prune.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/go/testdata/script/work_prune.txt b/src/cmd/go/testdata/script/work_prune.txt index 7347b312ee..f0fb073c4b 100644 --- a/src/cmd/go/testdata/script/work_prune.txt +++ b/src/cmd/go/testdata/script/work_prune.txt @@ -59,7 +59,7 @@ package b import "example.com/q" func TestB() { - q.PrintVersion + q.PrintVersion() } -- p/go.mod -- module example.com/p -- cgit v1.2.3-54-g00ecf From 42057e9848d40fc6181cd7a68fd788c652772b8d Mon Sep 17 00:00:00 2001 From: korzhao Date: Sat, 11 Sep 2021 01:14:54 +0800 Subject: cmd/compile: save the note of fields when translating struct Fixes #48317 Change-Id: I756ae6253022870071004332dd8f49169307f7e6 Reviewed-on: https://go-review.googlesource.com/c/go/+/349013 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Reviewed-by: Dan Scales Trust: Dan Scales --- src/cmd/compile/internal/typecheck/subr.go | 1 + test/typeparam/issue48317.go | 38 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 test/typeparam/issue48317.go diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 34f20879f1..5323872eaf 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -1312,6 +1312,7 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type { // the type param, not the instantiated type). newfields[i] = types.NewField(f.Pos, f.Sym, t2) newfields[i].Embedded = f.Embedded + newfields[i].Note = f.Note if f.IsDDD() { newfields[i].SetIsDDD(true) } diff --git a/test/typeparam/issue48317.go b/test/typeparam/issue48317.go new file mode 100644 index 0000000000..c8f088dc7a --- /dev/null +++ b/test/typeparam/issue48317.go @@ -0,0 +1,38 @@ +// run -gcflags="-G=3" + +// Copyright 2021 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 main + +import ( + "encoding/json" +) + +type A[T any] struct { + F1 string `json:"t1"` + F2 T `json:"t2"` + B B `json:"t3"` +} + +type B struct { + F4 int `json:"t4"` +} + +func a[T any]() { + data := `{"t1":"1","t2":2,"t3":{"t4":4}}` + a1 := A[T]{} + if err := json.Unmarshal([]byte(data), &a1); err != nil { + panic(err) + } + if bytes, err := json.Marshal(&a1); err != nil { + panic(err) + } else if string(bytes) != data { + panic(string(bytes)) + } +} + +func main() { + a[int]() +} -- cgit v1.2.3-54-g00ecf From 9a58aa267e3686c86d3e5bf1d14117a2a127838c Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 10 Sep 2021 17:06:43 -0700 Subject: spec: fix prose about terminating statements CL 85215 added prose to provide some minimal intuition for the definition of a "terminating statement". While the original definition was perfectly fine, the added prose was actually incorrect: If the terminating statement is a goto, it might jump to a labeled statement following that goto in the same block (it could be the very next statement), and thus a terminating statement does not in fact "prevent execution of all statements that lexically appear after it in the same block". Rather than explaining the special case for gotos with targets that are lexically following the goto in the same block, this CL opts for a simpler approach. Thanks to @3bodar (Github) for finding this. Fixes #48323. Change-Id: I8031346250341d038938a1ce6a75d3e687d32c37 Reviewed-on: https://go-review.googlesource.com/c/go/+/349172 Trust: Robert Griesemer Trust: Emmanuel Odeke Reviewed-by: Matthew Dempsky Reviewed-by: Emmanuel Odeke Reviewed-by: Rob Pike Reviewed-by: Ian Lance Taylor --- doc/go_spec.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index 3e97974d6d..6cc0b796b9 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -4561,9 +4561,8 @@ SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | S

Terminating statements

-A terminating statement prevents execution of all statements that lexically -appear after it in the same block. The following statements -are terminating: +A terminating statement interrupts the regular flow of control in +a block. The following statements are terminating:

    -- cgit v1.2.3-54-g00ecf From 146e8d4994052ee4a58bec7e2cf37e568ce1e4e5 Mon Sep 17 00:00:00 2001 From: Nevkontakte Date: Mon, 13 Sep 2021 22:40:30 +0000 Subject: reflect: use Value.Len instead of conversion to slice header This change is functionally equivalent, but reduces reliance on unsafe features. This would allow GopherJS to avoid an additional patch to the standard library we'd have to maintain in order to remain compatible with Go 1.17+. Change-Id: I4f113db0c572ec0b81ebfecf5a137145f6c8c41d GitHub-Last-Rev: 94ebb393bac93579b6455555822691c0d69e2d42 GitHub-Pull-Request: golang/go#48346 Reviewed-on: https://go-review.googlesource.com/c/go/+/349469 Reviewed-by: Keith Randall Reviewed-by: Ian Lance Taylor Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot --- src/reflect/value.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/reflect/value.go b/src/reflect/value.go index bc48a76ce6..33b81d7209 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -2940,8 +2940,7 @@ func (v Value) CanConvert(t Type) bool { // from slice to pointer-to-array. if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array { n := t.Elem().Len() - h := (*unsafeheader.Slice)(v.ptr) - if n > h.Len { + if n > v.Len() { return false } } @@ -3208,10 +3207,10 @@ func cvtStringRunes(v Value, t Type) Value { // convertOp: []T -> *[N]T func cvtSliceArrayPtr(v Value, t Type) Value { n := t.Elem().Len() - h := (*unsafeheader.Slice)(v.ptr) - if n > h.Len { - panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to pointer to array with length " + itoa.Itoa(n)) + if n > v.Len() { + panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to pointer to array with length " + itoa.Itoa(n)) } + h := (*unsafeheader.Slice)(v.ptr) return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)} } -- cgit v1.2.3-54-g00ecf From 71adc658deddc46a30bf690d37716d16cfccced7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 29 Apr 2021 19:08:54 -0700 Subject: runtime: change time.now to ABIInternal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces the number of instructions executed for time.now by nine, by eliminating the wrapper. Somehow BenchmarkNow is 0.2ns slower. On the other hand BenchmarkNowUnixNano is 0.8ns faster. name old time/op new time/op delta AfterFunc-12 66.7µs ± 4% 67.3µs ± 2% ~ (p=0.573 n=20+18) After-12 97.6µs ± 4% 97.4µs ± 4% ~ (p=0.758 n=20+20) Stop-12 66.7µs ±12% 64.8µs ±10% ~ (p=0.072 n=20+20) SimultaneousAfterFunc-12 109µs ± 0% 110µs ± 1% +1.47% (p=0.000 n=17+20) StartStop-12 31.9µs ±15% 32.7µs ±14% ~ (p=0.799 n=20+20) Reset-12 3.67µs ± 2% 3.68µs ± 2% ~ (p=0.132 n=20+20) Sleep-12 132µs ± 2% 133µs ± 2% +0.70% (p=0.035 n=20+19) Ticker-12 32.4µs ± 1% 32.3µs ± 2% ~ (p=0.270 n=20+19) TickerReset-12 3.71µs ± 2% 3.74µs ± 2% +0.89% (p=0.012 n=20+20) TickerResetNaive-12 65.7µs ±10% 67.2µs ±10% ~ (p=0.174 n=20+20) Now-12 29.6ns ± 1% 29.8ns ± 0% +0.78% (p=0.000 n=17+17) NowUnixNano-12 31.1ns ± 1% 30.3ns ± 0% -2.69% (p=0.000 n=19+18) NowUnixMilli-12 30.9ns ± 0% 31.1ns ± 0% +0.90% (p=0.000 n=18+20) NowUnixMicro-12 30.9ns ± 0% 31.1ns ± 1% +0.68% (p=0.000 n=20+18) Format-12 304ns ± 1% 301ns ± 2% -0.81% (p=0.004 n=18+19) FormatNow-12 187ns ± 2% 185ns ± 2% -0.90% (p=0.036 n=20+18) MarshalJSON-12 267ns ± 3% 265ns ± 3% -1.00% (p=0.004 n=18+18) MarshalText-12 267ns ± 2% 265ns ± 3% -0.87% (p=0.038 n=19+20) Parse-12 150ns ± 1% 149ns ± 1% -0.83% (p=0.000 n=18+20) ParseDuration-12 79.6ns ± 0% 80.1ns ± 1% +0.61% (p=0.000 n=20+20) Hour-12 4.42ns ± 1% 4.45ns ± 0% +0.83% (p=0.000 n=20+20) Second-12 4.42ns ± 0% 4.42ns ± 1% ~ (p=0.075 n=18+20) Year-12 11.1ns ± 1% 11.1ns ± 1% ~ (p=0.489 n=20+19) Day-12 14.8ns ± 1% 14.8ns ± 0% ~ (p=0.616 n=20+18) ISOWeek-12 17.2ns ± 1% 17.2ns ± 0% ~ (p=0.179 n=20+19) name old avg-late-ns new avg-late-ns delta ParallelTimerLatency-12 380k ± 4% 379k ± 3% ~ (p=0.879 n=20+19) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=1-12 137k ± 3% 137k ± 2% ~ (p=0.261 n=19+18) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=2-12 106k ±16% 95k ± 8% -9.76% (p=0.003 n=19+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=3-12 88.6k ±22% 74.6k ± 3% -15.78% (p=0.000 n=19+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=4-12 76.1k ±18% 70.8k ± 5% -7.04% (p=0.020 n=20+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=5-12 67.3k ±27% 65.6k ±13% ~ (p=0.211 n=16+18) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=6-12 59.5k ±24% 57.3k ±32% ~ (p=0.607 n=19+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=7-12 41.8k ±34% 46.2k ±33% +10.54% (p=0.039 n=17+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=8-12 57.5k ±37% 65.6k ±46% ~ (p=0.283 n=17+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=9-12 118k ±60% 136k ±59% ~ (p=0.169 n=19+18) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=10-12 3.66M ±236% 2.55M ±36% ~ (p=0.158 n=16+20) StaggeredTickerLatency/work-dur=2ms/tickers-per-P=1-12 81.7k ± 4% 80.7k ± 5% ~ (p=0.107 n=20+19) name old max-late-ns new max-late-ns delta ParallelTimerLatency-12 5.88M ±124% 7.28M ±183% ~ (p=0.640 n=20+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=1-12 384k ±17% 371k ±11% ~ (p=0.540 n=17+17) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=2-12 503k ±180% 373k ±19% ~ (p=0.057 n=17+18) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=3-12 519k ±129% 340k ±17% -34.47% (p=0.000 n=18+19) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=4-12 491k ±141% 341k ±26% -30.52% (p=0.015 n=18+17) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=5-12 457k ±123% 405k ±48% ~ (p=0.786 n=17+17) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=6-12 491k ±85% 502k ±74% ~ (p=0.916 n=18+19) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=7-12 572k ±100% 574k ±65% ~ (p=0.858 n=18+17) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=8-12 1.95M ±205% 1.65M ±155% ~ (p=0.641 n=18+19) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=9-12 7.77M ±104% 8.72M ±103% ~ (p=0.512 n=20+20) StaggeredTickerLatency/work-dur=300µs/tickers-per-P=10-12 29.5M ±187% 18.5M ±43% ~ (p=0.186 n=18+20) StaggeredTickerLatency/work-dur=2ms/tickers-per-P=1-12 981k ±14% 1033k ±12% +5.30% (p=0.048 n=20+18) Change-Id: Ie794a932a929b46053a6c3020b67d640b98d2335 Reviewed-on: https://go-review.googlesource.com/c/go/+/315369 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Michael Knyszek --- src/runtime/time_linux_amd64.s | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/runtime/time_linux_amd64.s b/src/runtime/time_linux_amd64.s index c88e92bd0c..67cfdd8fdf 100644 --- a/src/runtime/time_linux_amd64.s +++ b/src/runtime/time_linux_amd64.s @@ -12,14 +12,11 @@ #define SYS_clock_gettime 228 // func time.now() (sec int64, nsec int32, mono int64) -TEXT time·now(SB),NOSPLIT,$16-24 +TEXT time·now(SB),NOSPLIT,$16-24 MOVQ SP, R12 // Save old SP; R12 unchanged by C code. MOVQ g_m(R14), BX // BX unchanged by C code. - // Store CLOCK_REALTIME results directly to return space. - LEAQ sec+0(FP), SI - // Set vdsoPC and vdsoSP for SIGPROF traceback. // Save the old values on stack and restore them on exit, // so this function is reentrant. @@ -28,9 +25,10 @@ TEXT time·now(SB),NOSPLIT,$16-24 MOVQ CX, 0(SP) MOVQ DX, 8(SP) - MOVQ -8(SI), CX // Sets CX to function return address. + LEAQ sec+0(FP), DX + MOVQ -8(DX), CX // Sets CX to function return address. MOVQ CX, m_vdsoPC(BX) - MOVQ SI, m_vdsoSP(BX) + MOVQ DX, m_vdsoSP(BX) CMPQ R14, m_curg(BX) // Only switch if on curg. JNE noswitch @@ -39,10 +37,11 @@ TEXT time·now(SB),NOSPLIT,$16-24 MOVQ (g_sched+gobuf_sp)(DX), SP // Set SP to g0 stack noswitch: - SUBQ $16, SP // Space for monotonic time results + SUBQ $32, SP // Space for two time results ANDQ $~15, SP // Align for C code MOVL $0, DI // CLOCK_REALTIME + LEAQ 16(SP), SI MOVQ runtime·vdsoClockgettimeSym(SB), AX CMPQ AX, $0 JEQ fallback @@ -54,25 +53,27 @@ noswitch: CALL AX ret: - MOVQ 0(SP), AX // sec - MOVQ 8(SP), DX // nsec + MOVQ 16(SP), AX // realtime sec + MOVQ 24(SP), DI // realtime nsec (moved to BX below) + MOVQ 0(SP), CX // monotonic sec + IMULQ $1000000000, CX + MOVQ 8(SP), DX // monotonic nsec MOVQ R12, SP // Restore real SP + // Restore vdsoPC, vdsoSP // We don't worry about being signaled between the two stores. // If we are not in a signal handler, we'll restore vdsoSP to 0, // and no one will care about vdsoPC. If we are in a signal handler, // we cannot receive another signal. - MOVQ 8(SP), CX - MOVQ CX, m_vdsoSP(BX) - MOVQ 0(SP), CX - MOVQ CX, m_vdsoPC(BX) + MOVQ 8(SP), SI + MOVQ SI, m_vdsoSP(BX) + MOVQ 0(SP), SI + MOVQ SI, m_vdsoPC(BX) - // sec is in AX, nsec in DX - // return nsec in AX - IMULQ $1000000000, AX - ADDQ DX, AX - MOVQ AX, mono+16(FP) + // set result registers; AX is already correct + MOVQ DI, BX + ADDQ DX, CX RET fallback: -- cgit v1.2.3-54-g00ecf From 4a4221e8187189adcc6463d2d96fe2e8da290132 Mon Sep 17 00:00:00 2001 From: Alexander Melentyev Date: Sat, 11 Sep 2021 06:59:17 +0000 Subject: all: remove some unused code Change-Id: I519b8021fa79dccc5c0ee79134547491116fc4cc GitHub-Last-Rev: 48869f5434c1255d33c3a14714747509235c94df GitHub-Pull-Request: golang/go#48071 Reviewed-on: https://go-review.googlesource.com/c/go/+/346231 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Keith Randall --- src/database/sql/convert_test.go | 3 --- src/time/time.go | 1 - 2 files changed, 4 deletions(-) diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go index 2668a5ed5e..400da7ea57 100644 --- a/src/database/sql/convert_test.go +++ b/src/database/sql/convert_test.go @@ -51,9 +51,6 @@ var ( scanbytes []byte scanraw RawBytes scanint int - scanint8 int8 - scanint16 int16 - scanint32 int32 scanuint8 uint8 scanuint16 uint16 scanbool bool diff --git a/src/time/time.go b/src/time/time.go index 4ecc3d82dc..1919ebbc2c 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -425,7 +425,6 @@ const ( internalToUnix int64 = -unixToInternal wallToInternal int64 = (1884*365 + 1884/4 - 1884/100 + 1884/400) * secondsPerDay - internalToWall int64 = -wallToInternal ) // IsZero reports whether t represents the zero time instant, -- cgit v1.2.3-54-g00ecf From b8c802b1161528fcf8ba78b1dff5720dd5b4eb9f Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 10 Sep 2021 07:44:02 +0700 Subject: cmd/compile: prevent importReader reading type parameter twice The importReader always reads type parameter before declaring type stub declaration. Thus, for recursive type, the type parameter is going to be read twice, cause the bound more than once error. To fix this, only read the type parameter after declaring stub obj, thus r.doDecl can see the type was already inserted and terminate the recursive call earlier. Fixes #48280 Change-Id: I272e2f214f739fb8ec71a8628ba297477e1b7755 Reviewed-on: https://go-review.googlesource.com/c/go/+/349009 Trust: Cuong Manh Le Trust: Dan Scales Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Dan Scales --- src/cmd/compile/internal/importer/iimport.go | 12 ++++++------ test/typeparam/issue48280.dir/a.go | 11 +++++++++++ test/typeparam/issue48280.dir/main.go | 11 +++++++++++ test/typeparam/issue48280.go | 7 +++++++ 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 test/typeparam/issue48280.dir/a.go create mode 100644 test/typeparam/issue48280.dir/main.go create mode 100644 test/typeparam/issue48280.go diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index b61b1e97fb..7f7143dcfe 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -318,17 +318,17 @@ func (r *importReader) obj(name string) { r.declare(types2.NewFunc(pos, r.currPkg, name, sig)) case 'T', 'U': - var tparams []*types2.TypeParam - if tag == 'U' { - tparams = r.tparamList() - } - // Types can be recursive. We need to setup a stub // declaration before recursing. obj := types2.NewTypeName(pos, r.currPkg, name, nil) named := types2.NewNamed(obj, nil, nil) - named.SetTypeParams(tparams) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + named.SetTypeParams(tparams) + } underlying := r.p.typAt(r.uint64(), named).Underlying() named.SetUnderlying(underlying) diff --git a/test/typeparam/issue48280.dir/a.go b/test/typeparam/issue48280.dir/a.go new file mode 100644 index 0000000000..17859e6aa9 --- /dev/null +++ b/test/typeparam/issue48280.dir/a.go @@ -0,0 +1,11 @@ +// Copyright 2021 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 a + +type I[T I[T]] interface { + F() T +} + +type S struct{} diff --git a/test/typeparam/issue48280.dir/main.go b/test/typeparam/issue48280.dir/main.go new file mode 100644 index 0000000000..b9981c6f61 --- /dev/null +++ b/test/typeparam/issue48280.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 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 main + +import "a" + +func main() { + _ = a.S{} +} diff --git a/test/typeparam/issue48280.go b/test/typeparam/issue48280.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/issue48280.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From 2953cd00836323112846b21f60fa1d68aa0f9a77 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 10 Sep 2021 08:20:28 +0700 Subject: go/internal/gcimporter: prevent importReader reading type parameter twice This is port of CL 349009 to go/internal/gcimporter. Updates #48280 Change-Id: I7d40d8b67333538ca58fe012535d54e891d0ed16 Reviewed-on: https://go-review.googlesource.com/c/go/+/349010 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/go/internal/gcimporter/iimport.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index 039fc6a61b..56f6418d5e 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -284,6 +284,8 @@ type importReader struct { prevColumn int64 } +// obj reads import declaration for an object. It may not read +// the entire declaration, e.g, for recursive type. func (r *importReader) obj(name string) { tag := r.byte() pos := r.pos() @@ -309,16 +311,17 @@ func (r *importReader) obj(name string) { r.declare(types.NewFunc(pos, r.currPkg, name, sig)) case 'T', 'U': - var tparams []*types.TypeParam - if tag == 'U' { - tparams = r.tparamList() - } // Types can be recursive. We need to setup a stub // declaration before recursing. obj := types.NewTypeName(pos, r.currPkg, name, nil) named := types.NewNamed(obj, nil, nil) - named.SetTypeParams(tparams) + // Declare obj before calling r.tparamList, so the new type name is recognized + // if used in the constraint of one of its own typeparams (see #48280). r.declare(obj) + if tag == 'U' { + tparams := r.tparamList() + named.SetTypeParams(tparams) + } underlying := r.p.typAt(r.uint64(), named).Underlying() named.SetUnderlying(underlying) -- cgit v1.2.3-54-g00ecf From ee91bb83198f61aa8f26c3100ca7558d302c0a98 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 10 Sep 2021 08:34:03 +0700 Subject: cmd/compile: prevent typecheck importer reading type parameter twice This is a port of CL 349009 to typecheck importer. Fixes #48306 Change-Id: Iec3f078089346bd85f0ab739896e079940325011 Reviewed-on: https://go-review.googlesource.com/c/go/+/349011 Trust: Cuong Manh Le Trust: Dan Scales Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Dan Scales --- src/cmd/compile/internal/typecheck/iimport.go | 6 +----- src/go/internal/gcimporter/gcimporter_test.go | 2 -- test/run.go | 2 -- test/typeparam/issue48306.dir/a.go | 9 +++++++++ test/typeparam/issue48306.dir/main.go | 15 +++++++++++++++ test/typeparam/issue48306.go | 7 +++++++ 6 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 test/typeparam/issue48306.dir/a.go create mode 100644 test/typeparam/issue48306.dir/main.go create mode 100644 test/typeparam/issue48306.go diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 8bc098c2bd..6eec94a984 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -316,16 +316,12 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name { return n case 'T', 'U': - var rparams []*types.Type - if tag == 'U' { - rparams = r.typeList() - } - // Types can be recursive. We need to setup a stub // declaration before recursing. n := importtype(pos, sym) t := n.Type() if tag == 'U' { + rparams := r.typeList() t.SetRParams(rparams) } diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 478534daf2..9f4345d8f9 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -167,8 +167,6 @@ func TestImportTypeparamTests(t *testing.T) { skip := map[string]string{ "equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this. "nested.go": "fails to compile", // TODO(rfindley): investigate this. - - "issue46461.go": "known issue with type parameter constraints referring back to parameterized type", } for _, entry := range list { diff --git a/test/run.go b/test/run.go index d2b7b88768..790b54bfd2 100644 --- a/test/run.go +++ b/test/run.go @@ -2188,8 +2188,6 @@ var g3Failures = setOf( "typeparam/nested.go", // -G=3 doesn't support function-local types with generics - "typeparam/issue46461b.go", // -G=3 fails when type parameters refer back to the parameterized type itself - "typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops ) diff --git a/test/typeparam/issue48306.dir/a.go b/test/typeparam/issue48306.dir/a.go new file mode 100644 index 0000000000..739750b20b --- /dev/null +++ b/test/typeparam/issue48306.dir/a.go @@ -0,0 +1,9 @@ +// Copyright 2021 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 a + +type I[T I[T]] interface { + F() T +} diff --git a/test/typeparam/issue48306.dir/main.go b/test/typeparam/issue48306.dir/main.go new file mode 100644 index 0000000000..5d602fe07c --- /dev/null +++ b/test/typeparam/issue48306.dir/main.go @@ -0,0 +1,15 @@ +// Copyright 2021 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 main + +import "a" + +type S struct{} + +func (*S) F() *S { return nil } + +func main() { + var _ a.I[*S] = &S{} +} diff --git a/test/typeparam/issue48306.go b/test/typeparam/issue48306.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/issue48306.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From b3c6de9dcd9a7258615dea7ca0dcd00878f9027d Mon Sep 17 00:00:00 2001 From: "Paul E. Murphy" Date: Tue, 9 Mar 2021 16:55:31 -0600 Subject: cmd/internal/obj/ppc64: allow VR register arguments to VS registers Likewise, reorder register numbers such that extended mnemonics which use FPR arguments can be transparently encoded as a VSR argument for the move to/from VSR class of instructions. Specifically, ensure the following holds for all FPx and VRx constants: FPRx & 63 == x, and VRx & 63 == x + 32. This simplifies encoding machine instructions, and likewise helps ppc64 assembly writers to avoid hokey workarounds when switching from vector to vector-scalar register notation. Notably, many VSX instructions are limited to vector operands due to encoding restrictions. Secondly, this explicitly rejects dubious usages of the m[tf]vsr family of instructions which had previously been accepted. * Reject two GPR arguments for non-MTVSRDD opcodes. These have no defined behavior today, and may set RFU bits. e.g MTVSRD R1, R2, VS1 * Reject FPR destinations for MTVSRDD, and only accept with two GPR arguments. This copies two GPR values into either half of a VSR. e.g MTVSRDD R1, R2, F1 MTVSRDD R1, F1 Change-Id: If13dd88c3791d1892dbd18ef0e34675a5285fff9 Reviewed-on: https://go-review.googlesource.com/c/go/+/342929 Run-TryBot: Paul Murphy TryBot-Result: Go Bot Trust: Lynn Boger Reviewed-by: Cherry Mui --- src/cmd/asm/internal/asm/testdata/ppc64.s | 15 ++++++ src/cmd/internal/obj/ppc64/a.out.go | 10 ++-- src/cmd/internal/obj/ppc64/asm9.go | 85 +++++++++---------------------- src/cmd/internal/obj/ppc64/asm_test.go | 41 +++++++++++++++ 4 files changed, 87 insertions(+), 64 deletions(-) diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s index b6c0aa5035..28ceb621cb 100644 --- a/src/cmd/asm/internal/asm/testdata/ppc64.s +++ b/src/cmd/asm/internal/asm/testdata/ppc64.s @@ -649,6 +649,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 LXVB16X (R3)(R4), VS1 // 7c241ed8 LXVW4X (R3)(R4), VS1 // 7c241e18 LXV 16(R3), VS1 // f4230011 + LXV 16(R3), VS33 // f4230019 + LXV 16(R3), V1 // f4230019 LXVL R3, R4, VS1 // 7c23221a LXVLL R3, R4, VS1 // 7c23225a LXVX R3, R4, VS1 // 7c232218 @@ -668,8 +670,13 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 MTFPRD R3, F0 // 7c030166 MFVRD V0, R3 // 7c030067 MFVSRLD VS63,R4 // 7fe40267 + MFVSRLD V31,R4 // 7fe40267 MFVSRWZ VS33,R4 // 7c2400e7 + MFVSRWZ V1,R4 // 7c2400e7 MTVSRD R3, VS1 // 7c230166 + MTVSRDD R3, R4, VS1 // 7c232366 + MTVSRDD R3, R4, VS33 // 7c232367 + MTVSRDD R3, R4, V1 // 7c232367 MTVRD R3, V13 // 7da30167 MTVSRWA R4, VS31 // 7fe401a6 MTVSRWS R4, VS32 // 7c040327 @@ -678,6 +685,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 XXBRW VS1, VS2 // f04f0f6c XXBRH VS2, VS3 // f067176c XXLAND VS1, VS2, VS3 // f0611410 + XXLAND V1, V2, V3 // f0611417 + XXLAND VS33, VS34, VS35 // f0611417 XXLANDC VS1, VS2, VS3 // f0611450 XXLEQV VS0, VS1, VS2 // f0400dd0 XXLNAND VS0, VS1, VS2 // f0400d90 @@ -687,11 +696,17 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0 XXLORQ VS1, VS2, VS3 // f0611490 XXLXOR VS1, VS2, VS3 // f06114d0 XXSEL VS1, VS2, VS3, VS4 // f08110f0 + XXSEL VS33, VS34, VS35, VS36 // f08110ff + XXSEL V1, V2, V3, V4 // f08110ff XXMRGHW VS1, VS2, VS3 // f0611090 XXMRGLW VS1, VS2, VS3 // f0611190 XXSPLTW VS1, $1, VS2 // f0410a90 + XXSPLTW VS33, $1, VS34 // f0410a93 + XXSPLTW V1, $1, V2 // f0410a93 XXPERM VS1, VS2, VS3 // f06110d0 XXSLDWI VS1, VS2, $1, VS3 // f0611110 + XXSLDWI V1, V2, $1, V3 // f0611117 + XXSLDWI VS33, VS34, $1, VS35 // f0611117 XSCVDPSP VS1, VS2 // f0400c24 XVCVDPSP VS1, VS2 // f0400e24 XSCVSXDDP VS1, VS2 // f0400de0 diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go index e57beb3276..dda24a0b96 100644 --- a/src/cmd/internal/obj/ppc64/a.out.go +++ b/src/cmd/internal/obj/ppc64/a.out.go @@ -79,8 +79,10 @@ const ( REG_R30 REG_R31 - /* F0=4128 ... F31=4159 */ - REG_F0 + /* Align FPR and VSR vectors such that when masked with 0x3F they produce + an equivalent VSX register. */ + /* F0=4160 ... F31=4191 */ + REG_F0 = obj.RBasePPC64 + iota + 32 REG_F1 REG_F2 REG_F3 @@ -113,7 +115,7 @@ const ( REG_F30 REG_F31 - /* V0=4160 ... V31=4191 */ + /* V0=4192 ... V31=4223 */ REG_V0 REG_V1 REG_V2 @@ -147,7 +149,7 @@ const ( REG_V30 REG_V31 - /* VS0=4192 ... VS63=4255 */ + /* VS0=4224 ... VS63=4287 */ REG_VS0 REG_VS1 REG_VS2 diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go index e642413590..1d92c4866f 100644 --- a/src/cmd/internal/obj/ppc64/asm9.go +++ b/src/cmd/internal/obj/ppc64/asm9.go @@ -428,15 +428,13 @@ var optab = []Optab{ {as: ASTXSIWX, a1: C_VSREG, a6: C_SOREG, type_: 86, size: 4}, /* vsx scalar as integer store, xx1-form */ /* VSX move from VSR */ - {as: AMFVSRD, a1: C_VSREG, a6: C_REG, type_: 88, size: 4}, /* vsx move from vsr, xx1-form */ + {as: AMFVSRD, a1: C_VSREG, a6: C_REG, type_: 88, size: 4}, {as: AMFVSRD, a1: C_FREG, a6: C_REG, type_: 88, size: 4}, - {as: AMFVSRD, a1: C_VREG, a6: C_REG, type_: 88, size: 4}, /* VSX move to VSR */ - {as: AMTVSRD, a1: C_REG, a6: C_VSREG, type_: 88, size: 4}, /* vsx move to vsr, xx1-form */ - {as: AMTVSRD, a1: C_REG, a2: C_REG, a6: C_VSREG, type_: 88, size: 4}, - {as: AMTVSRD, a1: C_REG, a6: C_FREG, type_: 88, size: 4}, - {as: AMTVSRD, a1: C_REG, a6: C_VREG, type_: 88, size: 4}, + {as: AMTVSRD, a1: C_REG, a6: C_VSREG, type_: 104, size: 4}, + {as: AMTVSRD, a1: C_REG, a6: C_FREG, type_: 104, size: 4}, + {as: AMTVSRDD, a1: C_REG, a2: C_REG, a6: C_VSREG, type_: 104, size: 4}, /* VSX logical */ {as: AXXLAND, a1: C_VSREG, a2: C_VSREG, a6: C_VSREG, type_: 90, size: 4}, /* vsx and, xx3-form */ @@ -1036,13 +1034,14 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab { // c.ctxt.Logf("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4, a5, a6) ops := oprange[p.As&obj.AMask] c1 := &xcmp[a1] + c2 := &xcmp[a2] c3 := &xcmp[a3] c4 := &xcmp[a4] c5 := &xcmp[a5] c6 := &xcmp[a6] for i := range ops { op := &ops[i] - if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] && c4[op.a4] && c5[op.a5] && c6[op.a6] { + if c1[op.a1] && c2[op.a2] && c3[op.a3] && c4[op.a4] && c5[op.a5] && c6[op.a6] { p.Optab = uint16(cap(optab) - cap(ops) + i + 1) return op } @@ -1116,6 +1115,12 @@ func cmp(a int, b int) bool { return r0iszero != 0 /*TypeKind(100016)*/ } + case C_VSREG: + /* Allow any VR argument as a VSR operand. */ + if b == C_VREG { + return true + } + case C_ANY: return true } @@ -1594,7 +1599,6 @@ func buildop(ctxt *obj.Link) { opset(AMTVRD, r0) opset(AMTVSRWA, r0) opset(AMTVSRWZ, r0) - opset(AMTVSRDD, r0) opset(AMTVSRWS, r0) case AXXLAND: /* xxland, xxlandc, xxleqv, xxlnand */ @@ -1977,6 +1981,7 @@ func buildop(ctxt *obj.Link) { ACMPEQB, AECIWX, ACLRLSLWI, + AMTVSRDD, obj.ANOP, obj.ATEXT, obj.AUNDEF, @@ -2075,50 +2080,32 @@ func AOP_IR(op uint32, d uint32, simm uint32) uint32 { } /* XX1-form 3-register operands, 1 VSR operand */ -func AOP_XX1(op uint32, d uint32, a uint32, b uint32) uint32 { - /* For the XX-form encodings, we need the VSX register number to be exactly */ - /* between 0-63, so we can properly set the rightmost bits. */ - r := d - REG_VS0 +func AOP_XX1(op uint32, r uint32, a uint32, b uint32) uint32 { return op | (r&31)<<21 | (a&31)<<16 | (b&31)<<11 | (r&32)>>5 } /* XX2-form 3-register operands, 2 VSR operands */ -func AOP_XX2(op uint32, d uint32, a uint32, b uint32) uint32 { - xt := d - REG_VS0 - xb := b - REG_VS0 +func AOP_XX2(op uint32, xt uint32, a uint32, xb uint32) uint32 { return op | (xt&31)<<21 | (a&3)<<16 | (xb&31)<<11 | (xb&32)>>4 | (xt&32)>>5 } /* XX3-form 3 VSR operands */ -func AOP_XX3(op uint32, d uint32, a uint32, b uint32) uint32 { - xt := d - REG_VS0 - xa := a - REG_VS0 - xb := b - REG_VS0 +func AOP_XX3(op uint32, xt uint32, xa uint32, xb uint32) uint32 { return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5 } /* XX3-form 3 VSR operands + immediate */ -func AOP_XX3I(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 { - xt := d - REG_VS0 - xa := a - REG_VS0 - xb := b - REG_VS0 +func AOP_XX3I(op uint32, xt uint32, xa uint32, xb uint32, c uint32) uint32 { return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (c&3)<<8 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5 } /* XX4-form, 4 VSR operands */ -func AOP_XX4(op uint32, d uint32, a uint32, b uint32, c uint32) uint32 { - xt := d - REG_VS0 - xa := a - REG_VS0 - xb := b - REG_VS0 - xc := c - REG_VS0 +func AOP_XX4(op uint32, xt uint32, xa uint32, xb uint32, xc uint32) uint32 { return op | (xt&31)<<21 | (xa&31)<<16 | (xb&31)<<11 | (xc&31)<<6 | (xc&32)>>2 | (xa&32)>>3 | (xb&32)>>4 | (xt&32)>>5 } /* DQ-form, VSR register, register + offset operands */ -func AOP_DQ(op uint32, d uint32, a uint32, b uint32) uint32 { - /* For the DQ-form encodings, we need the VSX register number to be exactly */ - /* between 0-63, so we can properly set the SX bit. */ - r := d - REG_VS0 +func AOP_DQ(op uint32, xt uint32, a uint32, b uint32) uint32 { /* The EA for this instruction form is (RA) + DQ << 4, where DQ is a 12-bit signed integer. */ /* In order to match the output of the GNU objdump (and make the usage in Go asm easier), the */ /* instruction is called using the sign extended value (i.e. a valid offset would be -32752 or 32752, */ @@ -2126,7 +2113,7 @@ func AOP_DQ(op uint32, d uint32, a uint32, b uint32) uint32 { /* bits 0 to 3 in 'dq' need to be zero, otherwise this will generate an illegal instruction. */ /* If in doubt how this instruction form is encoded, refer to ISA 3.0b, pages 492 and 507. */ dq := b >> 4 - return op | (r&31)<<21 | (a&31)<<16 | (dq&4095)<<4 | (r&32)>>2 + return op | (xt&31)<<21 | (a&31)<<16 | (dq&4095)<<4 | (xt&32)>>2 } /* Z23-form, 3-register operands + CY field */ @@ -3586,33 +3573,8 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { /* 3-register operand order: (RB)(RA*1), XT */ o1 = AOP_XX1(c.oploadx(p.As), uint32(p.To.Reg), uint32(p.From.Index), uint32(p.From.Reg)) - case 88: /* VSX instructions, XX1-form */ - /* reg reg none OR reg reg reg */ - /* 3-register operand order: RA, RB, XT */ - /* 2-register operand order: XS, RA or RA, XT */ - xt := int32(p.To.Reg) - xs := int32(p.From.Reg) - /* We need to treat the special case of extended mnemonics that may have a FREG/VREG as an argument */ - if REG_V0 <= xt && xt <= REG_V31 { - /* Convert V0-V31 to VS32-VS63 */ - xt = xt + 64 - o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg)) - } else if REG_F0 <= xt && xt <= REG_F31 { - /* Convert F0-F31 to VS0-VS31 */ - xt = xt + 64 - o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg)) - } else if REG_VS0 <= xt && xt <= REG_VS63 { - o1 = AOP_XX1(c.oprrr(p.As), uint32(xt), uint32(p.From.Reg), uint32(p.Reg)) - } else if REG_V0 <= xs && xs <= REG_V31 { - /* Likewise for XS */ - xs = xs + 64 - o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg)) - } else if REG_F0 <= xs && xs <= REG_F31 { - xs = xs + 64 - o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg)) - } else if REG_VS0 <= xs && xs <= REG_VS63 { - o1 = AOP_XX1(c.oprrr(p.As), uint32(xs), uint32(p.To.Reg), uint32(p.Reg)) - } + case 88: /* VSX mfvsr* instructions, XX1-form XS,RA */ + o1 = AOP_XX1(c.oprrr(p.As), uint32(p.From.Reg), uint32(p.To.Reg), uint32(p.Reg)) case 89: /* VSX instructions, XX2-form */ /* reg none reg OR reg imm reg */ @@ -3743,6 +3705,9 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { mb := uint32(c.regoff(&p.RestArgs[0].Addr)) me := uint32(c.regoff(&p.RestArgs[1].Addr)) o1 = OP_RLW(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.Reg), uint32(p.From.Reg), mb, me) + + case 104: /* VSX mtvsr* instructions, XX1-form RA,RB,XT */ + o1 = AOP_XX1(c.oprrr(p.As), uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg)) } out[0] = o1 diff --git a/src/cmd/internal/obj/ppc64/asm_test.go b/src/cmd/internal/obj/ppc64/asm_test.go index 70dabc2017..b851d3c86b 100644 --- a/src/cmd/internal/obj/ppc64/asm_test.go +++ b/src/cmd/internal/obj/ppc64/asm_test.go @@ -107,3 +107,44 @@ func TestPCalign(t *testing.T) { t.Errorf("Invalid alignment not detected for PCALIGN\n") } } + +// Verify register constants are correctly aligned. Much of the ppc64 assembler assumes masking out significant +// bits will produce a valid register number: +// REG_Rx & 31 == x +// REG_Fx & 31 == x +// REG_Vx & 31 == x +// REG_VSx & 63 == x +// REG_SPRx & 1023 == x +// REG_CRx & 7 == x +// +// VR and FPR disjointly overlap VSR, interpreting as VSR registers should produce the correctly overlapped VSR. +// REG_FPx & 63 == x +// REG_Vx & 63 == x + 32 +func TestRegValueAlignment(t *testing.T) { + tstFunc := func(rstart, rend, msk, rout int) { + for i := rstart; i <= rend; i++ { + if i&msk != rout { + t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk) + } + rout++ + } + } + var testType = []struct { + rstart int + rend int + msk int + rout int + }{ + {REG_VS0, REG_VS63, 63, 0}, + {REG_R0, REG_R31, 31, 0}, + {REG_F0, REG_F31, 31, 0}, + {REG_V0, REG_V31, 31, 0}, + {REG_V0, REG_V31, 63, 32}, + {REG_F0, REG_F31, 63, 0}, + {REG_SPR0, REG_SPR0 + 1023, 1023, 0}, + {REG_CR0, REG_CR7, 7, 0}, + } + for _, t := range testType { + tstFunc(t.rstart, t.rend, t.msk, t.rout) + } +} -- cgit v1.2.3-54-g00ecf From 8699425b5527e24dc184d61d949d7f6db37c0c5c Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 14 Sep 2021 08:56:54 +0200 Subject: syscall: remove use of IN_KUBERNETES in test CL 201737 dropped the use of IN_KUBERNETES in tests, but it looks like it did not catch all occurrences. For #12815 For #34956 Change-Id: I72b89bfb850ba2890e9e6aa39b87167291ab7e9f Reviewed-on: https://go-review.googlesource.com/c/go/+/349789 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Brad Fitzpatrick --- src/syscall/exec_linux_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index 85b59ad00d..1555318eda 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -111,14 +111,6 @@ func checkUserNS(t *testing.T) { t.Skip("kernel doesn't support user namespaces") } } - - // When running under the Go continuous build, skip tests for - // now when under Kubernetes. (where things are root but not quite) - // Both of these are our own environment variables. - // See Issue 12815. - if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { - t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") - } } func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { @@ -201,14 +193,6 @@ func TestUnshare(t *testing.T) { t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") } - // When running under the Go continuous build, skip tests for - // now when under Kubernetes. (where things are root but not quite) - // Both of these are our own environment variables. - // See Issue 12815. - if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { - t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") - } - path := "/proc/net/dev" if _, err := os.Stat(path); err != nil { if os.IsNotExist(err) { -- cgit v1.2.3-54-g00ecf From 181e8cde301cd8205489e746334174fee7290c9b Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 14 Sep 2021 10:36:29 +0700 Subject: go/internal/gcimporter: remove outdated comment CL 349010 ported the fix from CL 349009, but forgot to remove the outdated comment from old solution. This CL removes that one. Change-Id: Ia401295e9d0984f4a088ddce5db09d306bfd89b6 Reviewed-on: https://go-review.googlesource.com/c/go/+/349729 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/go/internal/gcimporter/iimport.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index 56f6418d5e..d9174d470b 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -284,8 +284,6 @@ type importReader struct { prevColumn int64 } -// obj reads import declaration for an object. It may not read -// the entire declaration, e.g, for recursive type. func (r *importReader) obj(name string) { tag := r.byte() pos := r.pos() -- cgit v1.2.3-54-g00ecf From b2c04f0d48234765ce37bbb178bd174f3857929a Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Wed, 4 Aug 2021 16:07:47 -0400 Subject: runtime: avoid loop variable capture in test In TestSegv, the t.Run closure captures the loop variable 'test'. Since the subtest calls t.Parallel, the parent test is allowed to keep running, changing the loop variable and thus changing the value of 'test' in the subtest. Change-Id: I021ddc50304de08a341e6ffe486aa54e573d3b94 Reviewed-on: https://go-review.googlesource.com/c/go/+/339911 Trust: Michael Pratt Run-TryBot: Michael Pratt TryBot-Result: Go Bot Reviewed-by: Austin Clements Reviewed-by: Cherry Mui --- src/runtime/crash_cgo_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 5729942cee..ce7bed920f 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -591,6 +591,7 @@ func TestSegv(t *testing.T) { } for _, test := range []string{"Segv", "SegvInCgo"} { + test := test t.Run(test, func(t *testing.T) { t.Parallel() got := runTestProg(t, "testprogcgo", test) -- cgit v1.2.3-54-g00ecf From 3a72175cdcbfb64cca5968be52ac964f69d3a44a Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 10 Sep 2021 20:56:29 +0800 Subject: cmd/compile: fix test/typeparam/mdempsky/4.go for -G=3 Change-Id: I894ee000561a3c6afede8df697b1bce4576ceef0 Reviewed-on: https://go-review.googlesource.com/c/go/+/349012 Reviewed-by: Dan Scales Trust: Dan Scales Trust: Keith Randall --- src/cmd/compile/internal/typecheck/iimport.go | 22 +++++++++++++++++++--- test/run.go | 2 -- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 6eec94a984..b3a0eb8871 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -1166,10 +1166,26 @@ func (r *importReader) stmtList() []ir.Node { if n.Op() == ir.OBLOCK { n := n.(*ir.BlockStmt) list = append(list, n.List...) - } else { - list = append(list, n) + continue } - + if len(list) > 0 { + // check for an optional label that can only immediately + // precede a for/range/select/switch statement. + if last := list[len(list)-1]; last.Op() == ir.OLABEL { + label := last.(*ir.LabelStmt).Label + switch n.Op() { + case ir.OFOR: + n.(*ir.ForStmt).Label = label + case ir.ORANGE: + n.(*ir.RangeStmt).Label = label + case ir.OSELECT: + n.(*ir.SelectStmt).Label = label + case ir.OSWITCH: + n.(*ir.SwitchStmt).Label = label + } + } + } + list = append(list, n) } return list } diff --git a/test/run.go b/test/run.go index 790b54bfd2..3fb87af397 100644 --- a/test/run.go +++ b/test/run.go @@ -2187,8 +2187,6 @@ var g3Failures = setOf( "writebarrier.go", // correct diagnostics, but different lines (probably irgen's fault) "typeparam/nested.go", // -G=3 doesn't support function-local types with generics - - "typeparam/mdempsky/4.go", // -G=3 can't export functions with labeled breaks in loops ) var unifiedFailures = setOf( -- cgit v1.2.3-54-g00ecf From 137543bb93e15286b54d58d17d51e609ed49339a Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Tue, 14 Sep 2021 08:39:08 -0700 Subject: cmd/compile: set IsShape based on type being in the Shapes pkg Move ShapePkg to types, and change types.NewNamed to automatically set IsShape/HasShape if a type is in the shapes pkg. This means that imported shape types will automatically have the correct IsShape/HasShape flags, even though we are not explicitly exporting/importing those flags. Updates #48337 Change-Id: I8b6131a663205f73f395943c9d0c8bdb2a213401 Reviewed-on: https://go-review.googlesource.com/c/go/+/349869 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Dan Scales --- src/cmd/compile/internal/typecheck/subr.go | 10 +++++++--- src/cmd/compile/internal/types/type.go | 6 ++++++ test/typeparam/issue48337b.dir/a.go | 25 +++++++++++++++++++++++++ test/typeparam/issue48337b.dir/main.go | 11 +++++++++++ test/typeparam/issue48337b.go | 7 +++++++ 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/typeparam/issue48337b.dir/a.go create mode 100644 test/typeparam/issue48337b.dir/main.go create mode 100644 test/typeparam/issue48337b.go diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 5323872eaf..5854e3c458 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -1414,9 +1414,15 @@ func Shapify(t *types.Type) *types.Type { return s } - sym := shapePkg.Lookup(u.LinkString()) + sym := types.ShapePkg.Lookup(u.LinkString()) + if sym.Def != nil { + // Use any existing type with the same name + shaped[u] = sym.Def.Type() + return shaped[u] + } name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym) s := types.NewNamed(name) + sym.Def = name s.SetUnderlying(u) s.SetIsShape(true) s.SetHasShape(true) @@ -1427,5 +1433,3 @@ func Shapify(t *types.Type) *types.Type { } var shaped = map[*types.Type]*types.Type{} - -var shapePkg = types.NewPkg(".shape", ".shape") diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index eb70f7b9b4..392c54ba79 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -1706,6 +1706,10 @@ func NewNamed(obj TypeObject) *Type { t := newType(TFORW) t.sym = obj.Sym() t.nod = obj + if t.sym.Pkg == ShapePkg { + t.SetIsShape(true) + t.SetHasShape(true) + } return t } @@ -2182,3 +2186,5 @@ var ( ) var SimType [NTYPE]Kind + +var ShapePkg = NewPkg(".shape", ".shape") diff --git a/test/typeparam/issue48337b.dir/a.go b/test/typeparam/issue48337b.dir/a.go new file mode 100644 index 0000000000..a3c2e88a2f --- /dev/null +++ b/test/typeparam/issue48337b.dir/a.go @@ -0,0 +1,25 @@ +// Copyright 2021 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 a + +type Container[T any] struct { + X T +} + +func NewContainer[T any](x T) *Container[T] { + return &Container[T]{x} +} + +type MetaContainer struct { + C *Container[Value] +} + +type Value struct{} + +func NewMetaContainer() *MetaContainer { + c := NewContainer(Value{}) + // c := &Container[Value]{Value{}} // <-- this works + return &MetaContainer{c} +} diff --git a/test/typeparam/issue48337b.dir/main.go b/test/typeparam/issue48337b.dir/main.go new file mode 100644 index 0000000000..0b2814cbc0 --- /dev/null +++ b/test/typeparam/issue48337b.dir/main.go @@ -0,0 +1,11 @@ +// Copyright 2021 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 main + +import "a" + +func main() { + a.NewMetaContainer() +} diff --git a/test/typeparam/issue48337b.go b/test/typeparam/issue48337b.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/issue48337b.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From 2933c451a06ee0f97a698d1383cfbda988374137 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Fri, 10 Sep 2021 10:23:23 -0400 Subject: go/types: merge Named type loading and expansion Named type expansion and loading were conceptually similar: a mechanism for lazily resolving type information in a concurrency-safe manner. Unify them into a 'resolve' method, that delegates to a resolver func to produce type parameters, underlying, and methods. By leveraging the sync.Once field on Named for instance expansion, we get closer to making instance expansion concurrency-safe, and remove the requirement that instPos guard instantiation. This will be cleaned up in a follow-up CL. This also fixes #47887 by causing substituted type instances to be expanded (in the old code, this could be fixed by setting instPos when substituting). For #47910 Fixes #47887 Change-Id: Ifc52a420dde76e3a46ce494fea9bd289bc8aca4c Reviewed-on: https://go-review.googlesource.com/c/go/+/349410 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/decl.go | 4 +- src/go/types/instantiate.go | 3 +- src/go/types/lookup.go | 2 +- src/go/types/named.go | 114 ++++++++++--------------- src/go/types/object.go | 16 +++- src/go/types/signature.go | 2 +- src/go/types/subst.go | 39 +++++---- src/go/types/testdata/fixedbugs/issue47887.go2 | 28 ++++++ src/go/types/type.go | 2 +- 9 files changed, 114 insertions(+), 96 deletions(-) create mode 100644 src/go/types/testdata/fixedbugs/issue47887.go2 diff --git a/src/go/types/decl.go b/src/go/types/decl.go index c1506f6dbd..7f157f528a 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -316,7 +316,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.conf.Environment) + t.resolve(check.conf.Environment) // don't touch the type if it is from a different package or the Universe scope // (doing so would lead to a race condition - was issue #35049) if t.obj.pkg != check.pkg { @@ -773,7 +773,7 @@ func (check *Checker) collectMethods(obj *TypeName) { } if base != nil { - base.load() // TODO(mdempsky): Probably unnecessary. + base.resolve(nil) // TODO(mdempsky): Probably unnecessary. base.methods = append(base.methods, m) } } diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 50be07b8fd..b74f0db466 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -116,9 +116,10 @@ func (check *Checker) instance(pos token.Pos, typ Type, targs []Type, env *Envir } } tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) - named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded + named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved named.targs = NewTypeList(targs) named.instPos = &pos + named.resolver = expandNamed if env != nil { // It's possible that we've lost a race to add named to the environment. // In this case, use whichever instance is recorded in the environment. diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 4664a0b33b..cc7f24d97b 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -124,7 +124,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o seen[named] = true // look for a matching attached method - named.load() + named.resolve(nil) if i, m := lookupMethod(named.methods, pkg, name); m != nil { // potential match // caution: method may not have a proper signature yet diff --git a/src/go/types/named.go b/src/go/types/named.go index 74681ab2d4..fd9e1f4461 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -22,8 +22,9 @@ type Named struct { targs *TypeList // type arguments (after instantiation), or nil methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily - resolve func(*Named) ([]*TypeParam, Type, []*Func) - once sync.Once + // resolver may be provided to lazily resolve type parameters, underlying, and methods. + resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func) + once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing } // NewNamed returns a new named type for the given type name, underlying type, and associated methods. @@ -36,43 +37,22 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods) } -func (t *Named) load() *Named { - // If t is an instantiated type, it derives its methods and tparams from its - // base type. Since we expect type parameters and methods to be set after a - // call to load, we must load the base and copy here. - // - // underlying is set when t is expanded. - // - // By convention, a type instance is loaded iff its tparams are set. - if t.targs.Len() > 0 && t.tparams == nil { - t.orig.load() - t.tparams = t.orig.tparams - t.methods = t.orig.methods - } - if t.resolve == nil { +func (t *Named) resolve(env *Environment) *Named { + if t.resolver == nil { return t } t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to resolve anyway + // TODO(mdempsky): Since we're passing t to the resolver anyway // (necessary because types2 expects the receiver type for methods // on defined interface types to be the Named rather than the // underlying Interface), maybe it should just handle calling // SetTypeParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would + // methods would need to support reentrant calls though. It would // also make the API more future-proof towards further extensions // (like SetTypeParams). - - tparams, underlying, methods := t.resolve(t) - - switch underlying.(type) { - case nil, *Named: - panic("invalid underlying type") - } - - t.tparams = bindTParams(tparams) - t.underlying = underlying - t.methods = methods + t.tparams, t.underlying, t.methods = t.resolver(env, t) + t.fromRHS = t.underlying // for cycle detection }) return t } @@ -121,19 +101,19 @@ func (t *Named) _Orig() *Named { return t.orig } // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) TypeParams() *TypeParamList { return t.load().tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } // SetTypeParams sets the type parameters of the named type t. -func (t *Named) SetTypeParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } +func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. func (t *Named) TypeArgs() *TypeList { return t.targs } // NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.load().methods) } +func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). -func (t *Named) Method(i int) *Func { return t.load().methods[i] } +func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] } // SetUnderlying sets the underlying type and marks t as complete. func (t *Named) SetUnderlying(underlying Type) { @@ -143,18 +123,18 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.load().underlying = underlying + t.resolve(nil).underlying = underlying } // AddMethod adds method m unless it is already in the method list. func (t *Named) AddMethod(m *Func) { - t.load() + t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.load().expand(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve(nil).underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- @@ -240,43 +220,37 @@ func (n *Named) setUnderlying(typ Type) { } } -// expand ensures that the underlying type of n is instantiated. +// expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func (n *Named) expand(env *Environment) *Named { - if n.instPos != nil { - // n must be loaded before instantiation, in order to have accurate - // tparams. This is done implicitly by the call to n.TypeParams, but making - // it explicit is harmless: load is idempotent. - n.load() - var u Type - if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { - // TODO(rfindley): handling an optional Checker and Environment here (and - // in subst) feels overly complicated. Can we simplify? - if env == nil { - if n.check != nil { - env = n.check.conf.Environment - } else { - // If we're instantiating lazily, we might be outside the scope of a - // type-checking pass. In that case we won't have a pre-existing - // environment, but don't want to create a duplicate of the current - // instance in the process of expansion. - env = NewEnvironment() - } - h := env.typeHash(n.orig, n.targs.list()) - // add the instance to the environment to avoid infinite recursion. - // addInstance may return a different, existing instance, but we - // shouldn't return that instance from expand. - env.typeForHash(h, n) +func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { + n.orig.resolve(env) + + var u Type + if n.check.validateTArgLen(*n.instPos, n.orig.tparams.Len(), n.targs.Len()) { + // TODO(rfindley): handling an optional Checker and Environment here (and + // in subst) feels overly complicated. Can we simplify? + if env == nil { + if n.check != nil { + env = n.check.conf.Environment + } else { + // If we're instantiating lazily, we might be outside the scope of a + // type-checking pass. In that case we won't have a pre-existing + // environment, but don't want to create a duplicate of the current + // instance in the process of expansion. + env = NewEnvironment() } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TypeParams().list(), n.targs.list()), env) - } else { - u = Typ[Invalid] + h := env.typeHash(n.orig, n.targs.list()) + // add the instance to the environment to avoid infinite recursion. + // addInstance may return a different, existing instance, but we + // shouldn't return that instance from expand. + env.typeForHash(h, n) } - n.underlying = u - n.fromRHS = u - n.instPos = nil + u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + } else { + u = Typ[Invalid] } - return n + n.instPos = nil + return n.orig.tparams, u, n.orig.methods } // safeUnderlying returns the underlying of typ without expanding instances, to @@ -285,7 +259,7 @@ func (n *Named) expand(env *Environment) *Named { // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { if t, _ := typ.(*Named); t != nil { - return t.load().underlying + return t.resolve(nil).underlying } return typ.Underlying() } diff --git a/src/go/types/object.go b/src/go/types/object.go index b25fffdf5c..7f6f8a2550 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -232,9 +232,21 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { // _NewTypeNameLazy returns a new defined type like NewTypeName, but it // lazily calls resolve to finish constructing the Named object. -func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { +func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { obj := NewTypeName(pos, pkg, name, nil) - NewNamed(obj, nil, nil).resolve = resolve + + resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) { + tparams, underlying, methods := load(t) + + switch underlying.(type) { + case nil, *Named: + panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) + } + + return bindTParams(tparams), underlying, methods + } + + NewNamed(obj, nil, nil).resolver = resolve return obj } diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 37811828ee..bf6c775b89 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -200,7 +200,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast var err string switch T := rtyp.(type) { case *Named: - T.expand(nil) + T.resolve(check.conf.Environment) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 07fe6a6b6e..d9dab10e00 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -182,13 +182,19 @@ func (subst *subster) typ(typ Type) Type { } } - if t.TypeParams().Len() == 0 { + // subst is called by expandNamed, so in this function we need to be + // careful not to call any methods that would cause t to be expanded: doing + // so would result in deadlock. + // + // So we call t.orig.TypeParams() rather than t.TypeParams() here and + // below. + if t.orig.TypeParams().Len() == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } var newTArgs []Type - assert(t.targs.Len() == t.TypeParams().Len()) + assert(t.targs.Len() == t.orig.TypeParams().Len()) // already instantiated dump(">>> %s already instantiated", t) @@ -201,7 +207,7 @@ func (subst *subster) typ(typ Type) Type { if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.TypeParams().Len()) + newTArgs = make([]Type, t.orig.TypeParams().Len()) copy(newTArgs, t.targs.list()) } newTArgs[i] = new_targ @@ -221,25 +227,22 @@ func (subst *subster) typ(typ Type) Type { return named } - // Create a new named type and populate the environment to avoid endless + t.orig.resolve(subst.env) + // Create a new instance and populate the environment to avoid endless // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) - t.load() - // It's ok to provide a nil *Checker because the newly created type - // doesn't need to be (lazily) expanded; it's expanded below. - named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available - named.targs = NewTypeList(newTArgs) - subst.env.typeForHash(h, named) - t.expand(subst.env) // must happen after env update to avoid infinite recursion - - // do the substitution - dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs) - named.underlying = subst.typOrNil(t.underlying) - dump(">>> underlying: %v", named.underlying) + named := subst.check.instance(subst.pos, t.orig, newTArgs, subst.env).(*Named) + // TODO(rfindley): we probably don't need to resolve here. Investigate if + // this can be removed. + named.resolve(subst.env) assert(named.underlying != nil) - named.fromRHS = named.underlying // for consistency, though no cycle detection is necessary + + // Note that if we were to expose substitution more generally (not just in + // the context of a declaration), we'd have to substitute in + // named.underlying as well. + // + // But this is unnecessary for now. return named diff --git a/src/go/types/testdata/fixedbugs/issue47887.go2 b/src/go/types/testdata/fixedbugs/issue47887.go2 new file mode 100644 index 0000000000..4c4fc2fda8 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue47887.go2 @@ -0,0 +1,28 @@ +// Copyright 2021 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 p + +type Fooer[t any] interface { + foo(Barer[t]) +} +type Barer[t any] interface { + bar(Bazer[t]) +} +type Bazer[t any] interface { + Fooer[t] + baz(t) +} + +type Int int + +func (n Int) baz(int) {} +func (n Int) foo(b Barer[int]) { b.bar(n) } + +type F[t any] interface { f(G[t]) } +type G[t any] interface { g(H[t]) } +type H[t any] interface { F[t] } + +type T struct{} +func (n T) f(b G[T]) { b.g(n) } diff --git a/src/go/types/type.go b/src/go/types/type.go index b9634cf6f6..31149cfd36 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -114,7 +114,7 @@ func asInterface(t Type) *Interface { func asNamed(t Type) *Named { e, _ := t.(*Named) if e != nil { - e.expand(nil) + e.resolve(nil) } return e } -- cgit v1.2.3-54-g00ecf From bf26e43d0f9a6c9d43c206877917e66f0fc24a19 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Fri, 10 Sep 2021 15:12:57 -0400 Subject: go/types: eliminate Named.instPos We no longer need to use the nilness of Named.instPos to signal whether instance expansion has occurred, so remove it from the Named struct by instead closing over the instantiation position in the resolver. This means we cannot print instance markers for unexpanded instances: instances may escape the type checking pass without being fully expanded, and we can not check whether they have been expanded in a concurrency-safe way without introducing a more heavy-weight syncronization mechanism. With this change, instantiation should be concurrency safe, modulo bugs of course as we have little test coverage of concurrency (see #47729). Fixes #47910 Change-Id: Ifeef6df296f00105579554b333a44d08aae113c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/349411 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/internal/gcimporter/gcimporter_test.go | 3 --- src/go/types/environment.go | 7 ------- src/go/types/errors.go | 2 +- src/go/types/errors_test.go | 1 - src/go/types/instantiate.go | 5 +++-- src/go/types/named.go | 8 +++----- src/go/types/sizeof_test.go | 2 +- src/go/types/subst.go | 3 --- src/go/types/typestring.go | 10 ---------- 9 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 9f4345d8f9..3a9ed79df6 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -238,9 +238,6 @@ func TestImportTypeparamTests(t *testing.T) { func sanitizeObjectString(s string) string { var runes []rune for _, r := range s { - if r == '#' { - continue // trim instance markers - } if '₀' <= r && r < '₀'+10 { continue // trim type parameter subscripts } diff --git a/src/go/types/environment.go b/src/go/types/environment.go index 93383efe1a..61fc3c5348 100644 --- a/src/go/types/environment.go +++ b/src/go/types/environment.go @@ -50,13 +50,6 @@ func (env *Environment) typeHash(typ Type, targs []Type) string { h.typ(typ) } - if debug { - // there should be no instance markers in type hashes - for _, b := range buf.Bytes() { - assert(b != instanceMarker) - } - } - return buf.String() } diff --git a/src/go/types/errors.go b/src/go/types/errors.go index 933de93d85..2d48fe14da 100644 --- a/src/go/types/errors.go +++ b/src/go/types/errors.go @@ -265,7 +265,7 @@ func stripAnnotations(s string) string { var b strings.Builder for _, r := range s { // strip #'s and subscript digits - if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080 + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 b.WriteRune(r) } } diff --git a/src/go/types/errors_test.go b/src/go/types/errors_test.go index fdbe07cae0..942a9fdd4c 100644 --- a/src/go/types/errors_test.go +++ b/src/go/types/errors_test.go @@ -15,7 +15,6 @@ func TestStripAnnotations(t *testing.T) { {"foo", "foo"}, {"foo₀", "foo"}, {"foo(T₀)", "foo(T)"}, - {"#foo(T₀)", "foo(T)"}, } { got := stripAnnotations(test.in) if got != test.want { diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index b74f0db466..b178d1eb3f 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -118,8 +118,9 @@ func (check *Checker) instance(pos token.Pos, typ Type, targs []Type, env *Envir tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved named.targs = NewTypeList(targs) - named.instPos = &pos - named.resolver = expandNamed + named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { + return expandNamed(env, n, pos) + } if env != nil { // It's possible that we've lost a race to add named to the environment. // In this case, use whichever instance is recorded in the environment. diff --git a/src/go/types/named.go b/src/go/types/named.go index fd9e1f4461..943d52f0fe 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -17,7 +17,6 @@ type Named struct { orig *Named // original, uninstantiated type fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting) underlying Type // possibly a *Named during setup; never a *Named once set up completely - instPos *token.Pos // position information for lazy instantiation, or nil tparams *TypeParamList // type parameters, or nil targs *TypeList // type arguments (after instantiation), or nil methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily @@ -222,11 +221,11 @@ func (n *Named) setUnderlying(typ Type) { // expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { +func expandNamed(env *Environment, n *Named, instPos token.Pos) (*TypeParamList, Type, []*Func) { n.orig.resolve(env) var u Type - if n.check.validateTArgLen(*n.instPos, n.orig.tparams.Len(), n.targs.Len()) { + if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { // TODO(rfindley): handling an optional Checker and Environment here (and // in subst) feels overly complicated. Can we simplify? if env == nil { @@ -245,11 +244,10 @@ func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { // shouldn't return that instance from expand. env.typeForHash(h, n) } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) } else { u = Typ[Invalid] } - n.instPos = nil return n.orig.tparams, u, n.orig.methods } diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go index f64f732884..f418e037a9 100644 --- a/src/go/types/sizeof_test.go +++ b/src/go/types/sizeof_test.go @@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 44, 88}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 72, 136}, + {Named{}, 68, 128}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, {top{}, 0, 0}, diff --git a/src/go/types/subst.go b/src/go/types/subst.go index d9dab10e00..a063dd0a07 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -8,9 +8,6 @@ package types import "go/token" -// TODO(rFindley) decide error codes for the errors in this file, and check -// if error spans can be improved - type substMap map[*TypeParam]Type // makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 7e971c0325..eadc50a754 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -65,9 +65,6 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { newTypeWriter(buf, qf).signature(sig) } -// instanceMarker is the prefix for an instantiated type in unexpanded form. -const instanceMarker = '#' - type typeWriter struct { buf *bytes.Buffer seen map[Type]bool @@ -226,13 +223,6 @@ func (w *typeWriter) typ(typ Type) { } case *Named: - // Instance markers indicate unexpanded instantiated - // types. Write them to aid debugging, but don't write - // them when we need an instance hash: whether a type - // is fully expanded or not doesn't matter for identity. - if w.env == nil && t.instPos != nil { - w.byte(instanceMarker) - } w.typePrefix(t) w.typeName(t.obj) if t.targs != nil { -- cgit v1.2.3-54-g00ecf From a0f3129466744d22746b93e65ff3714f1507b6a3 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Fri, 10 Sep 2021 16:28:01 -0400 Subject: go/types: instantiate methods when instantiating Named types In the API proposal we decided that instantiation must also instantiate methods. This CL does that, and eliminates the special handling for lazy instantiation in lookupMethod. It is possible that we expand an instance before all method signatures have been type-checked, so for simplicity we introduce a new flag on Func, 'isIncompleteMethod', which controls whether we must fully substitute methods before using them. We could avoid this flag by using some convention for the structure of an incomplete method (such as the receiver has no position), but in practice using a flag was cleaner and didn't increase the size of the Func struct. Updates #47916 Change-Id: I352baa6664cd07f61b06924744382897805f9d29 Reviewed-on: https://go-review.googlesource.com/c/go/+/349412 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/call.go | 48 ------------------------ src/go/types/decl.go | 6 +++ src/go/types/infer.go | 1 + src/go/types/instantiate_test.go | 39 ++++++++++++++++++++ src/go/types/lookup.go | 24 ------------ src/go/types/named.go | 79 ++++++++++++++++++++++++++++++++++------ src/go/types/object.go | 5 ++- src/go/types/sizeof_test.go | 2 +- src/go/types/subst.go | 4 +- 9 files changed, 119 insertions(+), 89 deletions(-) diff --git a/src/go/types/call.go b/src/go/types/call.go index 4de5fed46e..4d14e31730 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -532,54 +532,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // methods may not have a fully set up signature yet if m, _ := obj.(*Func); m != nil { check.objDecl(m, nil) - // If m has a parameterized receiver type, infer the type arguments from - // the actual receiver provided and then substitute the type parameters in - // the signature accordingly. - // TODO(gri) factor this code out - sig := m.typ.(*Signature) - if sig.RecvTypeParams().Len() > 0 { - // For inference to work, we must use the receiver type - // matching the receiver in the actual method declaration. - // If the method is embedded, the matching receiver is the - // embedded struct or interface that declared the method. - // Traverse the embedding to find that type (issue #44688). - recv := x.typ - for i := 0; i < len(index)-1; i++ { - // The embedded type is either a struct or a pointer to - // a struct except for the last one (which we don't need). - recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ - } - - // The method may have a pointer receiver, but the actually provided receiver - // may be a (hopefully addressable) non-pointer value, or vice versa. Here we - // only care about inferring receiver type parameters; to make the inference - // work, match up pointer-ness of receiver and argument. - if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) { - if ptrRecv { - recv = NewPointer(recv) - } else { - recv = recv.(*Pointer).base - } - } - // Disable reporting of errors during inference below. If we're unable to infer - // the receiver type arguments here, the receiver must be be otherwise invalid - // and an error has been reported elsewhere. - arg := operand{mode: variable, expr: x.expr, typ: recv} - targs := check.infer(m, sig.RecvTypeParams().list(), nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */) - if targs == nil { - // We may reach here if there were other errors (see issue #40056). - goto Error - } - // Don't modify m. Instead - for now - make a copy of m and use that instead. - // (If we modify m, some tests will fail; possibly because the m is in use.) - // TODO(gri) investigate and provide a correct explanation here - copy := *m - copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.RecvTypeParams().list(), targs), nil) - obj = © - } - // TODO(gri) we also need to do substitution for parameterized interface methods - // (this breaks code in testdata/linalg.go2 at the moment) - // 12/20/2019: Is this TODO still correct? } if x.mode == typexpr { diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 7f157f528a..0fdcfa8023 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -65,6 +65,12 @@ func (check *Checker) objDecl(obj Object, def *Named) { }() } + // Funcs with m.instRecv set have not yet be completed. Complete them now + // so that they have a type when objDecl exits. + if m, _ := obj.(*Func); m != nil && m.instRecv != nil { + check.completeMethod(check.conf.Environment, m) + } + // Checking the declaration of obj means inferring its type // (and possibly its value, for constants). // An object's type (and thus the object) may be in one of diff --git a/src/go/types/infer.go b/src/go/types/infer.go index 7314a614d0..18c5119177 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -28,6 +28,7 @@ import ( // // Constraint type inference is used after each step to expand the set of type arguments. // +// TODO(rfindley): remove the report parameter: is no longer needed. func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) { if debug { defer func() { diff --git a/src/go/types/instantiate_test.go b/src/go/types/instantiate_test.go index 0b09bfebe3..851800e76d 100644 --- a/src/go/types/instantiate_test.go +++ b/src/go/types/instantiate_test.go @@ -70,3 +70,42 @@ func TestInstantiateNonEquality(t *testing.T) { t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2) } } + +func TestMethodInstantiation(t *testing.T) { + const prefix = genericPkg + `p + +type T[P any] struct{} + +var X T[int] + +` + tests := []struct { + decl string + want string + }{ + {"func (r T[P]) m() P", "func (T[int]).m() int"}, + {"func (r T[P]) m(P)", "func (T[int]).m(int)"}, + {"func (r T[P]) m() func() P", "func (T[int]).m() func() int"}, + {"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"}, + {"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"}, + {"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"}, + {"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"}, + } + + for _, test := range tests { + src := prefix + test.decl + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + typ := pkg.Scope().Lookup("X").Type().(*Named) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + m, _ := obj.(*Func) + if m == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + if got := ObjectString(m, RelativeTo(pkg)); got != test.want { + t.Errorf("instantiated %q, want %q", got, test.want) + } + } +} diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index cc7f24d97b..a270159499 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -6,8 +6,6 @@ package types -import "go/token" - // Internal use of LookupFieldOrMethod: If the obj result is a method // associated with a concrete (non-interface) type, the method's signature // may not be fully set up. Call Checker.objDecl(obj, nil) before accessing @@ -342,8 +340,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, } // A concrete type implements T if it implements all methods of T. - Vd, _ := deref(V) - Vn := asNamed(Vd) for _, m := range T.typeSet().methods { // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) @@ -378,26 +374,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, panic("method with type parameters") } - // If V is a (instantiated) generic type, its methods are still - // parameterized using the original (declaration) receiver type - // parameters (subst simply copies the existing method list, it - // does not instantiate the methods). - // In order to compare the signatures, substitute the receiver - // type parameters of ftyp with V's instantiation type arguments. - // This lazily instantiates the signature of method f. - if Vn != nil && Vn.TypeParams().Len() > 0 { - // Be careful: The number of type arguments may not match - // the number of receiver parameters. If so, an error was - // reported earlier but the length discrepancy is still - // here. Exit early in this case to prevent an assertion - // failure in makeSubstMap. - // TODO(gri) Can we avoid this check by fixing the lengths? - if len(ftyp.RecvTypeParams().list()) != Vn.targs.Len() { - return - } - ftyp = check.subst(token.NoPos, ftyp, makeSubstMap(ftyp.RecvTypeParams().list(), Vn.targs.list()), nil).(*Signature) - } - // If the methods have type parameters we don't care whether they // are the same or not, as long as they match up. Use unification // to see if they can be made to match. diff --git a/src/go/types/named.go b/src/go/types/named.go index 943d52f0fe..66ae012379 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -221,16 +221,17 @@ func (n *Named) setUnderlying(typ Type) { // expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func expandNamed(env *Environment, n *Named, instPos token.Pos) (*TypeParamList, Type, []*Func) { +func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) { n.orig.resolve(env) - var u Type - if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { + check := n.check + + if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { // TODO(rfindley): handling an optional Checker and Environment here (and // in subst) feels overly complicated. Can we simplify? if env == nil { - if n.check != nil { - env = n.check.conf.Environment + if check != nil { + env = check.conf.Environment } else { // If we're instantiating lazily, we might be outside the scope of a // type-checking pass. In that case we won't have a pre-existing @@ -239,16 +240,72 @@ func expandNamed(env *Environment, n *Named, instPos token.Pos) (*TypeParamList, env = NewEnvironment() } h := env.typeHash(n.orig, n.targs.list()) - // add the instance to the environment to avoid infinite recursion. - // addInstance may return a different, existing instance, but we - // shouldn't return that instance from expand. + // ensure that an instance is recorded for h to avoid infinite recursion. env.typeForHash(h, n) } - u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) + underlying = n.check.subst(instPos, n.orig.underlying, smap, env) + for i := 0; i < n.orig.NumMethods(); i++ { + origm := n.orig.Method(i) + + // During type checking origm may not have a fully set up type, so defer + // instantiation of its signature until later. + m := NewFunc(origm.pos, origm.pkg, origm.name, nil) + m.hasPtrRecv = origm.hasPtrRecv + // Setting instRecv here allows us to complete later (we need the + // instRecv to get targs and the original method). + m.instRecv = n + + methods = append(methods, m) + } + } else { + underlying = Typ[Invalid] + } + + // Methods should not escape the type checker API without being completed. If + // we're in the context of a type checking pass, we need to defer this until + // later (not all methods may have types). + completeMethods := func() { + for _, m := range methods { + if m.instRecv != nil { + check.completeMethod(env, m) + } + } + } + if check != nil { + check.later(completeMethods) } else { - u = Typ[Invalid] + completeMethods() } - return n.orig.tparams, u, n.orig.methods + + return n.orig.tparams, underlying, methods +} + +func (check *Checker) completeMethod(env *Environment, m *Func) { + assert(m.instRecv != nil) + rtyp := m.instRecv + m.instRecv = nil + m.setColor(black) + + assert(rtyp.TypeArgs().Len() > 0) + + // Look up the original method. + _, orig := lookupMethod(rtyp.orig.methods, rtyp.obj.pkg, m.name) + assert(orig != nil) + if check != nil { + check.objDecl(orig, nil) + } + origSig := orig.typ.(*Signature) + if origSig.RecvTypeParams().Len() != rtyp.targs.Len() { + m.typ = origSig // or new(Signature), but we can't use Typ[Invalid]: Funcs must have Signature type + return // error reported elsewhere + } + + smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list()) + sig := check.subst(orig.pos, origSig, smap, env).(*Signature) + sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) + + m.typ = sig } // safeUnderlying returns the underlying of typ without expanding instances, to diff --git a/src/go/types/object.go b/src/go/types/object.go index 7f6f8a2550..454b714458 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -317,7 +317,8 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa // An abstract method may belong to many interfaces due to embedding. type Func struct { object - hasPtrRecv bool // only valid for methods that don't have a type yet + instRecv *Named // if non-nil, the receiver type for an incomplete instance method + hasPtrRecv bool // only valid for methods that don't have a type yet } // NewFunc returns a new function with the given signature, representing @@ -328,7 +329,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func { if sig != nil { typ = sig } - return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false} + return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil, false} } // FullName returns the package- or receiver-type-qualified name of diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go index f418e037a9..0e3c0064a0 100644 --- a/src/go/types/sizeof_test.go +++ b/src/go/types/sizeof_test.go @@ -40,7 +40,7 @@ func TestSizeof(t *testing.T) { {Const{}, 48, 88}, {TypeName{}, 40, 72}, {Var{}, 44, 80}, - {Func{}, 44, 80}, + {Func{}, 48, 88}, {Label{}, 44, 80}, {Builtin{}, 44, 80}, {Nil{}, 40, 72}, diff --git a/src/go/types/subst.go b/src/go/types/subst.go index a063dd0a07..3491541dcb 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -70,7 +70,6 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, env *Environ env = NewEnvironment() } subst.env = env - return subst.typ(typ) } @@ -125,8 +124,7 @@ func (subst *subster) typ(typ Type) Type { if recv != t.recv || params != t.params || results != t.results { return &Signature{ rparams: t.rparams, - // TODO(rFindley) why can't we nil out tparams here, rather than in - // instantiate above? + // TODO(rFindley) why can't we nil out tparams here, rather than in instantiate? tparams: t.tparams, scope: t.scope, recv: recv, -- cgit v1.2.3-54-g00ecf From cb4e1de0213c836983d1b441386c53e1a66e1b0a Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sat, 11 Sep 2021 23:06:41 -0400 Subject: go/types: minor cleanup of instantiation This CL addresses a couple TODOs related to instantiation: - factor out resolving the best environment - don't eagerly resolve substituted instances Change-Id: I4a5de7ea7939b6f272991071f591d622dec04b53 Reviewed-on: https://go-review.googlesource.com/c/go/+/349429 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/named.go | 38 ++++++++++++++++++++++---------------- src/go/types/subst.go | 31 +++++++------------------------ 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/go/types/named.go b/src/go/types/named.go index 66ae012379..00fde16445 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -219,6 +219,21 @@ func (n *Named) setUnderlying(typ Type) { } } +// bestEnv returns the best available environment. In order of preference: +// - the given env, if non-nil +// - the Checker env, if check is non-nil +// - a new environment +func (check *Checker) bestEnv(env *Environment) *Environment { + if env != nil { + return env + } + if check != nil { + assert(check.conf.Environment != nil) + return check.conf.Environment + } + return NewEnvironment() +} + // expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) { @@ -227,24 +242,15 @@ func expandNamed(env *Environment, n *Named, instPos token.Pos) (tparams *TypePa check := n.check if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { - // TODO(rfindley): handling an optional Checker and Environment here (and - // in subst) feels overly complicated. Can we simplify? - if env == nil { - if check != nil { - env = check.conf.Environment - } else { - // If we're instantiating lazily, we might be outside the scope of a - // type-checking pass. In that case we won't have a pre-existing - // environment, but don't want to create a duplicate of the current - // instance in the process of expansion. - env = NewEnvironment() - } - h := env.typeHash(n.orig, n.targs.list()) - // ensure that an instance is recorded for h to avoid infinite recursion. - env.typeForHash(h, n) - } + // We must always have an env, to avoid infinite recursion. + env = check.bestEnv(env) + h := env.typeHash(n.orig, n.targs.list()) + // ensure that an instance is recorded for h to avoid infinite recursion. + env.typeForHash(h, n) + smap := makeSubstMap(n.orig.tparams.list(), n.targs.list()) underlying = n.check.subst(instPos, n.orig.underlying, smap, env) + for i := 0; i < n.orig.NumMethods(); i++ { origm := n.orig.Method(i) diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 3491541dcb..16aafd622e 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -52,24 +52,12 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, env *Environ } // general case - var subst subster - subst.pos = pos - subst.smap = smap - - if check != nil { - subst.check = check - if env == nil { - env = check.conf.Environment - } - } - if env == nil { - // If we don't have a *Checker and its global type map, - // use a local version. Besides avoiding duplicate work, - // the type map prevents infinite recursive substitution - // for recursive types (example: type T[P any] *T[P]). - env = NewEnvironment() + subst := subster{ + pos: pos, + smap: smap, + check: check, + env: check.bestEnv(env), } - subst.env = env return subst.typ(typ) } @@ -227,11 +215,8 @@ func (subst *subster) typ(typ Type) Type { // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - named := subst.check.instance(subst.pos, t.orig, newTArgs, subst.env).(*Named) - // TODO(rfindley): we probably don't need to resolve here. Investigate if - // this can be removed. - named.resolve(subst.env) - assert(named.underlying != nil) + t.orig.resolve(subst.env) + return subst.check.instance(subst.pos, t.orig, newTArgs, subst.env) // Note that if we were to expose substitution more generally (not just in // the context of a declaration), we'd have to substitute in @@ -239,8 +224,6 @@ func (subst *subster) typ(typ Type) Type { // // But this is unnecessary for now. - return named - case *TypeParam: return subst.smap.lookup(t) -- cgit v1.2.3-54-g00ecf From 0bb40b08c4884952d7facce3135ff9e50847763f Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Sun, 12 Sep 2021 14:33:04 -0400 Subject: go/types: implement Identical for *Union types This aligns with the API proposal (#47916). Change-Id: I732e5b107e729718ed37e053ad3f434993a97ecd Reviewed-on: https://go-review.googlesource.com/c/go/+/349413 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/api_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ src/go/types/predicates.go | 9 +++++++++ 2 files changed, 51 insertions(+) diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 49c054bd7d..4472748685 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -1621,6 +1621,48 @@ func TestIdentical_issue15173(t *testing.T) { } } +func TestIdenticalUnions(t *testing.T) { + tname := NewTypeName(token.NoPos, nil, "myInt", nil) + myInt := NewNamed(tname, Typ[Int], nil) + tmap := map[string]*Term{ + "int": NewTerm(false, Typ[Int]), + "~int": NewTerm(true, Typ[Int]), + "string": NewTerm(false, Typ[String]), + "~string": NewTerm(true, Typ[String]), + "myInt": NewTerm(false, myInt), + } + makeUnion := func(s string) *Union { + parts := strings.Split(s, "|") + var terms []*Term + for _, p := range parts { + term := tmap[p] + if term == nil { + t.Fatalf("missing term %q", p) + } + terms = append(terms, term) + } + return NewUnion(terms) + } + for _, test := range []struct { + x, y string + want bool + }{ + // These tests are just sanity checks. The tests for type sets and + // interfaces provide much more test coverage. + {"int|~int", "~int", true}, + {"myInt|~int", "~int", true}, + {"int|string", "string|int", true}, + {"int|int|string", "string|int", true}, + {"myInt|string", "int|string", false}, + } { + x := makeUnion(test.x) + y := makeUnion(test.y) + if got := Identical(x, y); got != test.want { + t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got) + } + } +} + func TestIssue15305(t *testing.T) { const src = "package p; func f() int16; var _ = f(undef)" fset := token.NewFileSet() diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 9aa565b68a..a5d4be9bcc 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -6,6 +6,8 @@ package types +import "go/token" + // isNamed reports whether typ has a name. // isNamed may be called with types that are not fully set up. func isNamed(typ Type) bool { @@ -225,6 +227,13 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { identical(x.results, y.results, cmpTags, p) } + case *Union: + if y, _ := y.(*Union); y != nil { + xset := computeUnionTypeSet(nil, token.NoPos, x) + yset := computeUnionTypeSet(nil, token.NoPos, y) + return xset.terms.equal(yset.terms) + } + case *Interface: // Two interface types are identical if they describe the same type sets. // With the existing implementation restriction, this simplifies to: -- cgit v1.2.3-54-g00ecf From c7f2f51fed15b410dea5f608420858b401887d0a Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Mon, 13 Sep 2021 10:58:25 -0700 Subject: cmd/go: remove subcommand prefix from error messages For example, errors that started before with "go mod download: " now start with "go: " instead. Previously, we had a mix of errors with and without subcommand prefixes, even in packages like modload that ostensibly aren't tied to any specific command. This change makes usage more consistent, which makes refactoring much easier. These prefixes didn't add useful information: the user should know the subcommand they just ran. But see CL 347152 for an attempt at making the opposite change: always printing the subcommand prefix. Note that there are a number of errors that don't start with "go: " or any subcommand prefix. This CL doesn't affect those. Change-Id: I16430d8c39ea3f4d0870f55a5205f06fb21943c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/349597 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/base/tool.go | 2 +- src/cmd/go/internal/bug/bug.go | 2 +- src/cmd/go/internal/clean/clean.go | 16 +++---- src/cmd/go/internal/envcmd/env.go | 32 +++++++------- src/cmd/go/internal/get/get.go | 10 ++--- src/cmd/go/internal/list/list.go | 10 ++--- src/cmd/go/internal/modcmd/download.go | 10 ++--- src/cmd/go/internal/modcmd/edit.go | 50 +++++++++++----------- src/cmd/go/internal/modcmd/editwork.go | 26 +++++------ src/cmd/go/internal/modcmd/graph.go | 2 +- src/cmd/go/internal/modcmd/init.go | 2 +- src/cmd/go/internal/modcmd/tidy.go | 2 +- src/cmd/go/internal/modcmd/vendor.go | 34 +++++++-------- src/cmd/go/internal/modcmd/verify.go | 2 +- src/cmd/go/internal/modcmd/why.go | 4 +- src/cmd/go/internal/modget/get.go | 34 +++++++-------- src/cmd/go/internal/modget/query.go | 8 ++-- src/cmd/go/internal/modload/load.go | 6 +-- src/cmd/go/internal/run/run.go | 12 +++--- src/cmd/go/internal/test/test.go | 2 +- src/cmd/go/internal/test/testflag.go | 2 +- src/cmd/go/internal/tool/tool.go | 6 +-- src/cmd/go/internal/version/version.go | 10 ++++- src/cmd/go/internal/vet/vet.go | 2 +- src/cmd/go/internal/vet/vetflag.go | 4 +- src/cmd/go/internal/work/action.go | 4 +- src/cmd/go/internal/work/build.go | 26 +++++------ src/cmd/go/internal/work/init.go | 15 +++---- src/cmd/go/testdata/script/build_i_deprecate.txt | 6 +-- src/cmd/go/testdata/script/env_unset.txt | 6 +-- src/cmd/go/testdata/script/env_write.txt | 18 ++++---- src/cmd/go/testdata/script/get_go_file.txt | 12 +++--- .../script/get_insecure_no_longer_supported.txt | 4 +- src/cmd/go/testdata/script/gopath_install.txt | 2 +- src/cmd/go/testdata/script/gopath_local.txt | 4 +- src/cmd/go/testdata/script/govcs.txt | 26 +++++------ src/cmd/go/testdata/script/list_shadow.txt | 2 +- src/cmd/go/testdata/script/mod_bad_domain.txt | 2 +- src/cmd/go/testdata/script/mod_dot.txt | 6 +-- src/cmd/go/testdata/script/mod_download.txt | 10 ++--- src/cmd/go/testdata/script/mod_edit.txt | 10 ++--- src/cmd/go/testdata/script/mod_get_changes.txt | 12 +++--- .../testdata/script/mod_get_deprecate_install.txt | 4 +- src/cmd/go/testdata/script/mod_get_downgrade.txt | 4 +- .../testdata/script/mod_get_downgrade_missing.txt | 2 +- src/cmd/go/testdata/script/mod_get_go_file.txt | 10 ++--- src/cmd/go/testdata/script/mod_get_main.txt | 14 +++--- src/cmd/go/testdata/script/mod_get_newcycle.txt | 2 +- src/cmd/go/testdata/script/mod_get_nopkgs.txt | 4 +- src/cmd/go/testdata/script/mod_get_patch.txt | 6 +-- src/cmd/go/testdata/script/mod_get_patchcycle.txt | 2 +- src/cmd/go/testdata/script/mod_get_patchmod.txt | 2 +- src/cmd/go/testdata/script/mod_get_patterns.txt | 4 +- src/cmd/go/testdata/script/mod_get_pkgtags.txt | 4 +- src/cmd/go/testdata/script/mod_get_private_vcs.txt | 2 +- src/cmd/go/testdata/script/mod_get_replaced.txt | 2 +- src/cmd/go/testdata/script/mod_get_split.txt | 2 +- src/cmd/go/testdata/script/mod_get_svn.txt | 2 +- src/cmd/go/testdata/script/mod_get_wild.txt | 2 +- src/cmd/go/testdata/script/mod_getmode_vendor.txt | 6 +-- src/cmd/go/testdata/script/mod_gonoproxy.txt | 4 +- .../go/testdata/script/mod_install_pkg_version.txt | 22 +++++----- src/cmd/go/testdata/script/mod_invalid_path.txt | 2 +- .../go/testdata/script/mod_invalid_path_plus.txt | 2 +- src/cmd/go/testdata/script/mod_invalid_version.txt | 32 +++++++------- src/cmd/go/testdata/script/mod_list.txt | 4 +- src/cmd/go/testdata/script/mod_list_sums.txt | 2 +- .../testdata/script/mod_list_update_nolatest.txt | 2 +- src/cmd/go/testdata/script/mod_load_badchain.txt | 6 +-- src/cmd/go/testdata/script/mod_outside.txt | 6 +-- .../go/testdata/script/mod_prefer_compatible.txt | 2 +- src/cmd/go/testdata/script/mod_proxy_invalid.txt | 4 +- src/cmd/go/testdata/script/mod_query.txt | 2 +- src/cmd/go/testdata/script/mod_query_empty.txt | 4 +- src/cmd/go/testdata/script/mod_query_exclude.txt | 2 +- src/cmd/go/testdata/script/mod_query_main.txt | 10 ++--- src/cmd/go/testdata/script/mod_replace_gopkgin.txt | 2 +- .../go/testdata/script/mod_retract_fix_version.txt | 2 +- .../go/testdata/script/mod_retract_pseudo_base.txt | 2 +- src/cmd/go/testdata/script/mod_run_nonmain.txt | 2 +- src/cmd/go/testdata/script/mod_run_pkg_version.txt | 6 +-- src/cmd/go/testdata/script/mod_sum_readonly.txt | 6 +-- src/cmd/go/testdata/script/mod_sumdb.txt | 2 +- src/cmd/go/testdata/script/mod_sumdb_file_path.txt | 2 +- src/cmd/go/testdata/script/mod_tidy_compat.txt | 2 +- .../testdata/script/mod_tidy_compat_ambiguous.txt | 2 +- src/cmd/go/testdata/script/mod_tidy_too_new.txt | 4 +- src/cmd/go/testdata/script/mod_upgrade_patch.txt | 6 +-- src/cmd/go/testdata/script/mod_vendor.txt | 6 +-- src/cmd/go/testdata/script/mod_vendor_auto.txt | 8 ++-- src/cmd/go/testdata/script/mod_vendor_embed.txt | 4 +- src/cmd/go/testdata/script/run_wildcard.txt | 2 +- src/cmd/go/testdata/script/test_flag.txt | 6 +-- src/cmd/go/testdata/script/test_race_install.txt | 2 +- 94 files changed, 353 insertions(+), 348 deletions(-) diff --git a/src/cmd/go/internal/base/tool.go b/src/cmd/go/internal/base/tool.go index d0da65e03c..f927016965 100644 --- a/src/cmd/go/internal/base/tool.go +++ b/src/cmd/go/internal/base/tool.go @@ -36,7 +36,7 @@ func Tool(toolName string) string { } // Give a nice message if there is no tool with that name. if _, err := os.Stat(toolPath); err != nil { - fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName) SetExitStatus(2) Exit() } diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go index 307527c695..a81ca7d8c3 100644 --- a/src/cmd/go/internal/bug/bug.go +++ b/src/cmd/go/internal/bug/bug.go @@ -40,7 +40,7 @@ func init() { func runBug(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go bug: bug takes no arguments") + base.Fatalf("go: bug takes no arguments") } var buf bytes.Buffer buf.WriteString(bugHeader) diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go index fd4cb20559..1089211f0c 100644 --- a/src/cmd/go/internal/clean/clean.go +++ b/src/cmd/go/internal/clean/clean.go @@ -144,7 +144,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { // This also mimics what os.RemoveAll(dir) would do. if err := os.RemoveAll(d); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -157,7 +157,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildN { if err := os.RemoveAll(logFile); err != nil && !printedErrors { printedErrors = true - base.Errorf("go clean -cache: %v", err) + base.Errorf("go: %v", err) } } } @@ -187,7 +187,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { } if err != nil { if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) { - base.Errorf("go clean -testcache: %v", err) + base.Errorf("go: %v", err) } } } @@ -195,14 +195,14 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) { if cleanModcache { if cfg.GOMODCACHE == "" { - base.Fatalf("go clean -modcache: no module cache") + base.Fatalf("go: cannot clean -modcache without a module cache") } if cfg.BuildN || cfg.BuildX { b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE) } if !cfg.BuildN { if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil { - base.Errorf("go clean -modcache: %v", err) + base.Errorf("go: %v", err) } } } @@ -245,7 +245,7 @@ func clean(p *load.Package) { } dirs, err := os.ReadDir(p.Dir) if err != nil { - base.Errorf("go clean %s: %v", p.Dir, err) + base.Errorf("go: %s: %v", p.Dir, err) return } @@ -334,7 +334,7 @@ func clean(p *load.Package) { } } if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } } continue @@ -386,5 +386,5 @@ func removeFile(f string) { return } } - base.Errorf("go clean: %v", err) + base.Errorf("go: %v", err) } diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index d23d539141..1eb773407e 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -193,13 +193,13 @@ func argKey(arg string) string { func runEnv(ctx context.Context, cmd *base.Command, args []string) { if *envJson && *envU { - base.Fatalf("go env: cannot use -json with -u") + base.Fatalf("go: cannot use -json with -u") } if *envJson && *envW { - base.Fatalf("go env: cannot use -json with -w") + base.Fatalf("go: cannot use -json with -w") } if *envU && *envW { - base.Fatalf("go env: cannot use -u with -w") + base.Fatalf("go: cannot use -u with -w") } // Handle 'go env -w' and 'go env -u' before calling buildcfg.Check, @@ -277,7 +277,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) { func runEnvW(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -w: no KEY=VALUE arguments given") + base.Fatalf("go: no KEY=VALUE arguments given") } osEnv := make(map[string]string) for _, e := range cfg.OrigEnv { @@ -289,14 +289,14 @@ func runEnvW(args []string) { for _, arg := range args { i := strings.Index(arg, "=") if i < 0 { - base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg) + base.Fatalf("go: arguments must be KEY=VALUE: invalid argument: %s", arg) } key, val := arg[:i], arg[i+1:] if err := checkEnvWrite(key, val); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } if _, ok := add[key]; ok { - base.Fatalf("go env -w: multiple values for key: %s", key) + base.Fatalf("go: multiple values for key: %s", key) } add[key] = val if osVal := osEnv[key]; osVal != "" && osVal != val { @@ -305,13 +305,13 @@ func runEnvW(args []string) { } if err := checkBuildConfig(add, nil); err != nil { - base.Fatalf("go env -w: %v", err) + base.Fatalf("go: %v", err) } gotmp, okGOTMP := add["GOTMPDIR"] if okGOTMP { if !filepath.IsAbs(gotmp) && gotmp != "" { - base.Fatalf("go env -w: GOTMPDIR must be an absolute path") + base.Fatalf("go: GOTMPDIR must be an absolute path") } } @@ -321,18 +321,18 @@ func runEnvW(args []string) { func runEnvU(args []string) { // Process and sanity-check command line. if len(args) == 0 { - base.Fatalf("go env -u: no arguments given") + base.Fatalf("go: 'go env -u' requires an argument") } del := make(map[string]bool) for _, arg := range args { if err := checkEnvWrite(arg, ""); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } del[arg] = true } if err := checkBuildConfig(nil, del); err != nil { - base.Fatalf("go env -u: %v", err) + base.Fatalf("go: %v", err) } updateEnvFile(nil, del) @@ -416,7 +416,7 @@ func printEnvAsJSON(env []cfg.EnvVar) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", "\t") if err := enc.Encode(m); err != nil { - base.Fatalf("go env -json: %s", err) + base.Fatalf("go: %s", err) } } @@ -494,11 +494,11 @@ func checkEnvWrite(key, val string) error { func updateEnvFile(add map[string]string, del map[string]bool) { file, err := cfg.EnvFile() if file == "" { - base.Fatalf("go env: cannot find go env config: %v", err) + base.Fatalf("go: cannot find go env config: %v", err) } data, err := os.ReadFile(file) if err != nil && (!os.IsNotExist(err) || len(add) == 0) { - base.Fatalf("go env: reading go env config: %v", err) + base.Fatalf("go: reading go env config: %v", err) } lines := strings.SplitAfter(string(data), "\n") @@ -556,7 +556,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) { os.MkdirAll(filepath.Dir(file), 0777) err = os.WriteFile(file, data, 0666) if err != nil { - base.Fatalf("go env: writing go env config: %v", err) + base.Fatalf("go: writing go env config: %v", err) } } } diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go index 075594b271..b79d3ba86f 100644 --- a/src/cmd/go/internal/get/get.go +++ b/src/cmd/go/internal/get/get.go @@ -114,16 +114,16 @@ func init() { func runGet(ctx context.Context, cmd *base.Command, args []string) { if cfg.ModulesEnabled { // Should not happen: main.go should install the separate module-enabled get code. - base.Fatalf("go get: modules not implemented") + base.Fatalf("go: modules not implemented") } work.BuildInit() if *getF && !*getU { - base.Fatalf("go get: cannot use -f flag without -u") + base.Fatalf("go: cannot use -f flag without -u") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } // Disable any prompting for passwords by Git itself. @@ -214,11 +214,11 @@ func downloadPaths(patterns []string) []string { // if the argument has no slash or refers to an existing file. if strings.HasSuffix(arg, ".go") { if !strings.Contains(arg, "/") { - base.Errorf("go get %s: arguments must be package or module paths", arg) + base.Errorf("go: %s: arguments must be package or module paths", arg) continue } if fi, err := os.Stat(arg); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg) } } } diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 704d61e7c1..821e622abb 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.go @@ -427,12 +427,12 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } if modload.Init(); !modload.Enabled() { - base.Fatalf("go list -m: not using modules") + base.Fatalf("go: list -m cannot be used with GO111MODULE=off") } modload.LoadModFile(ctx) // Sets cfg.BuildMod as a side-effect. if cfg.BuildMod == "vendor" { - const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" + const actionDisabledFormat = "go: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)" if *listVersions { base.Fatalf(actionDisabledFormat, "determine available versions") @@ -471,11 +471,11 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { if !*listE { for _, m := range mods { if m.Error != nil { - base.Errorf("go list -m: %v", m.Error.Err) + base.Errorf("go: %v", m.Error.Err) } } if err != nil { - base.Errorf("go list -m: %v", err) + base.Errorf("go: %v", err) } base.ExitIfErrors() } @@ -711,7 +711,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { } rmods, err := modload.ListModules(ctx, args, mode) if err != nil && !*listE { - base.Errorf("go list -retracted: %v", err) + base.Errorf("go: %v", err) } for i, arg := range args { rmod := rmods[i] diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go index ff56d05116..0f64785656 100644 --- a/src/cmd/go/internal/modcmd/download.go +++ b/src/cmd/go/internal/modcmd/download.go @@ -87,7 +87,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { // Check whether modules are enabled and whether we're in a module. modload.ForceUseModules = true if !modload.HasModRoot() && len(args) == 0 { - base.Fatalf("go mod download: no modules specified (see 'go help mod download')") + base.Fatalf("go: no modules specified (see 'go help mod download')") } haveExplicitArgs := len(args) > 0 if !haveExplicitArgs { @@ -106,7 +106,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { for _, arg := range args { switch arg { case mainModule.Path, targetAtUpgrade, targetAtPatch: - os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n") + os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n") } } } @@ -192,7 +192,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { for _, m := range mods { b, err := json.MarshalIndent(m, "", "\t") if err != nil { - base.Fatalf("go mod download: %v", err) + base.Fatalf("go: %v", err) } os.Stdout.Write(append(b, '\n')) if m.Error != "" { @@ -202,7 +202,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { } else { for _, m := range mods { if m.Error != "" { - base.Errorf("go mod download: %v", m.Error) + base.Errorf("go: %v", m.Error) } } base.ExitIfErrors() @@ -222,6 +222,6 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) { // (after we've written the checksums for the modules that were downloaded // successfully). if infosErr != nil { - base.Errorf("go mod download: %v", infosErr) + base.Errorf("go: %v", infosErr) } } diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go index bb3d521092..e5182a9590 100644 --- a/src/cmd/go/internal/modcmd/edit.go +++ b/src/cmd/go/internal/modcmd/edit.go @@ -171,15 +171,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { len(edits) > 0 if !anyFlags { - base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').") + base.Fatalf("go: no flags specified (see 'go help mod edit').") } if *editJSON && *editPrint { - base.Fatalf("go mod edit: cannot use both -json and -print") + base.Fatalf("go: cannot use both -json and -print") } if len(args) > 1 { - base.Fatalf("go mod edit: too many arguments") + base.Fatalf("go: too many arguments") } var gomod string if len(args) == 1 { @@ -190,7 +190,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { if *editModule != "" { if err := module.CheckImportPath(*editModule); err != nil { - base.Fatalf("go mod: invalid -module: %v", err) + base.Fatalf("go: invalid -module: %v", err) } } @@ -264,15 +264,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) { func parsePathVersion(flag, arg string) (path, version string) { i := strings.Index(arg, "@") if i < 0 { - base.Fatalf("go mod: -%s=%s: need path@version", flag, arg) + base.Fatalf("go: -%s=%s: need path@version", flag, arg) } path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } if !allowedVersionArg(version) { - base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version) + base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version) } return path, version @@ -281,11 +281,11 @@ func parsePathVersion(flag, arg string) (path, version string) { // parsePath parses -flag=arg expecting arg to be path (not path@version). func parsePath(flag, arg string) (path string) { if strings.Contains(arg, "@") { - base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg) + base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg) } path = arg if err := module.CheckImportPath(path); err != nil { - base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err) + base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err) } return path } @@ -350,7 +350,7 @@ func flagRequire(arg string) { path, version := parsePathVersion("require", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddRequire(path, version); err != nil { - base.Fatalf("go mod: -require=%s: %v", arg, err) + base.Fatalf("go: -require=%s: %v", arg, err) } }) } @@ -360,7 +360,7 @@ func flagDropRequire(arg string) { path := parsePath("droprequire", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropRequire(path); err != nil { - base.Fatalf("go mod: -droprequire=%s: %v", arg, err) + base.Fatalf("go: -droprequire=%s: %v", arg, err) } }) } @@ -370,7 +370,7 @@ func flagExclude(arg string) { path, version := parsePathVersion("exclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.AddExclude(path, version); err != nil { - base.Fatalf("go mod: -exclude=%s: %v", arg, err) + base.Fatalf("go: -exclude=%s: %v", arg, err) } }) } @@ -380,7 +380,7 @@ func flagDropExclude(arg string) { path, version := parsePathVersion("dropexclude", arg) edits = append(edits, func(f *modfile.File) { if err := f.DropExclude(path, version); err != nil { - base.Fatalf("go mod: -dropexclude=%s: %v", arg, err) + base.Fatalf("go: -dropexclude=%s: %v", arg, err) } }) } @@ -389,27 +389,27 @@ func flagDropExclude(arg string) { func flagReplace(arg string) { var i int if i = strings.Index(arg, "="); i < 0 { - base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) } old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if strings.HasPrefix(new, ">") { - base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg) + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) } oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } newPath, newVersion, err := parsePathVersionOptional("new", new, true) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } if newPath == new && !modfile.IsDirectoryPath(new) { - base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg) + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) } edits = append(edits, func(f *modfile.File) { if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } }) } @@ -418,11 +418,11 @@ func flagReplace(arg string) { func flagDropReplace(arg string) { path, version, err := parsePathVersionOptional("old", arg, true) if err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropReplace(path, version); err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } }) } @@ -431,11 +431,11 @@ func flagDropReplace(arg string) { func flagRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.AddRetract(vi, ""); err != nil { - base.Fatalf("go mod: -retract=%s: %v", arg, err) + base.Fatalf("go: -retract=%s: %v", arg, err) } }) } @@ -444,11 +444,11 @@ func flagRetract(arg string) { func flagDropRetract(arg string) { vi, err := parseVersionInterval(arg) if err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } edits = append(edits, func(f *modfile.File) { if err := f.DropRetract(vi); err != nil { - base.Fatalf("go mod: -dropretract=%s: %v", arg, err) + base.Fatalf("go: -dropretract=%s: %v", arg, err) } }) } diff --git a/src/cmd/go/internal/modcmd/editwork.go b/src/cmd/go/internal/modcmd/editwork.go index 29895b1620..235c655387 100644 --- a/src/cmd/go/internal/modcmd/editwork.go +++ b/src/cmd/go/internal/modcmd/editwork.go @@ -118,15 +118,15 @@ func runEditwork(ctx context.Context, cmd *base.Command, args []string) { len(workedits) > 0 if !anyFlags { - base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').") + base.Fatalf("go: no flags specified (see 'go help mod edit').") } if *editworkJSON && *editworkPrint { - base.Fatalf("go mod edit: cannot use both -json and -print") + base.Fatalf("go: cannot use both -json and -print") } if len(args) > 1 { - base.Fatalf("go mod edit: too many arguments") + base.Fatalf("go: 'go mod editwork' accepts at most one argument") } var gowork string if len(args) == 1 { @@ -199,7 +199,7 @@ func flagEditworkDirectory(arg string) { } f.AddDirectory(modload.ToDirectoryPath(arg), modulePath) if err := f.AddDirectory(modload.ToDirectoryPath(arg), ""); err != nil { - base.Fatalf("go mod: -directory=%s: %v", arg, err) + base.Fatalf("go: -directory=%s: %v", arg, err) } }) } @@ -208,7 +208,7 @@ func flagEditworkDirectory(arg string) { func flagEditworkDropDirectory(arg string) { workedits = append(workedits, func(f *modfile.WorkFile) { if err := f.DropDirectory(modload.ToDirectoryPath(arg)); err != nil { - base.Fatalf("go mod: -dropdirectory=%s: %v", arg, err) + base.Fatalf("go: -dropdirectory=%s: %v", arg, err) } }) } @@ -217,27 +217,27 @@ func flagEditworkDropDirectory(arg string) { func flagEditworkReplace(arg string) { var i int if i = strings.Index(arg, "="); i < 0 { - base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg) + base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg) } old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:]) if strings.HasPrefix(new, ">") { - base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg) + base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg) } oldPath, oldVersion, err := parsePathVersionOptional("old", old, false) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } newPath, newVersion, err := parsePathVersionOptional("new", new, true) if err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } if newPath == new && !modfile.IsDirectoryPath(new) { - base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg) + base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg) } workedits = append(workedits, func(f *modfile.WorkFile) { if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil { - base.Fatalf("go mod: -replace=%s: %v", arg, err) + base.Fatalf("go: -replace=%s: %v", arg, err) } }) } @@ -246,11 +246,11 @@ func flagEditworkReplace(arg string) { func flagEditworkDropReplace(arg string) { path, version, err := parsePathVersionOptional("old", arg, true) if err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } workedits = append(workedits, func(f *modfile.WorkFile) { if err := f.DropReplace(path, version); err != nil { - base.Fatalf("go mod: -dropreplace=%s: %v", arg, err) + base.Fatalf("go: -dropreplace=%s: %v", arg, err) } }) } diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go index 2cbabae044..9b6aa1fb14 100644 --- a/src/cmd/go/internal/modcmd/graph.go +++ b/src/cmd/go/internal/modcmd/graph.go @@ -49,7 +49,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) { modload.InitWorkfile() if len(args) > 0 { - base.Fatalf("go mod graph: graph takes no arguments") + base.Fatalf("go: 'go mod graph' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go index 958c3066ac..bc4620a2a8 100644 --- a/src/cmd/go/internal/modcmd/init.go +++ b/src/cmd/go/internal/modcmd/init.go @@ -39,7 +39,7 @@ func init() { func runInit(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 1 { - base.Fatalf("go mod init: too many arguments") + base.Fatalf("go: 'go mod init' accepts at most one argument") } var modPath string if len(args) == 1 { diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go index fe25507e94..57d303a13c 100644 --- a/src/cmd/go/internal/modcmd/tidy.go +++ b/src/cmd/go/internal/modcmd/tidy.go @@ -95,7 +95,7 @@ func (f *goVersionFlag) Set(s string) error { func runTidy(ctx context.Context, cmd *base.Command, args []string) { if len(args) > 0 { - base.Fatalf("go mod tidy: no arguments allowed") + base.Fatalf("go: 'go mod tidy' accepts no arguments") } // Tidy aims to make 'go test' reproducible for any package in 'all', so we diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go index 1effcea1a0..92348b8897 100644 --- a/src/cmd/go/internal/modcmd/vendor.go +++ b/src/cmd/go/internal/modcmd/vendor.go @@ -59,7 +59,7 @@ func init() { func runVendor(ctx context.Context, cmd *base.Command, args []string) { if len(args) != 0 { - base.Fatalf("go mod vendor: vendor takes no arguments") + base.Fatalf("go: 'go mod vendor' accepts no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot @@ -76,7 +76,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { vdir := filepath.Join(modload.VendorDir()) if err := os.RemoveAll(vdir); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } modpkgs := make(map[module.Version][]string) @@ -178,11 +178,11 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) { } if err := os.MkdirAll(vdir, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } @@ -250,7 +250,7 @@ func vendorPkg(vdir, pkg string) { embedPatterns := str.StringList(bp.EmbedPatterns, bp.TestEmbedPatterns, bp.XTestEmbedPatterns) embeds, err := load.ResolveEmbed(bp.Dir, embedPatterns) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, embed := range embeds { embedDst := filepath.Join(dst, embed) @@ -261,21 +261,21 @@ func vendorPkg(vdir, pkg string) { // Copy the file as is done by copyDir below. r, err := os.Open(filepath.Join(src, embed)) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(filepath.Dir(embedDst), 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } w, err := os.Create(embedDst) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } @@ -354,7 +354,7 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { if strings.HasSuffix(info.Name(), ".go") { f, err := fsys.Open(filepath.Join(dir, info.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } defer f.Close() @@ -376,10 +376,10 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool { func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, copiedFiles map[string]bool) { files, err := os.ReadDir(src) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if err := os.MkdirAll(dst, 0777); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } for _, file := range files { if file.IsDir() || !file.Type().IsRegular() || !match(src, file) { @@ -388,20 +388,20 @@ func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, cop copiedFiles[file.Name()] = true r, err := os.Open(filepath.Join(src, file.Name())) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } dstPath := filepath.Join(dst, file.Name()) copiedFiles[dstPath] = true w, err := os.Create(dstPath) if err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } if _, err := io.Copy(w, r); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } r.Close() if err := w.Close(); err != nil { - base.Fatalf("go mod vendor: %v", err) + base.Fatalf("go: %v", err) } } } diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go index 14c4d76bc3..3f0c005d5d 100644 --- a/src/cmd/go/internal/modcmd/verify.go +++ b/src/cmd/go/internal/modcmd/verify.go @@ -47,7 +47,7 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) { if len(args) != 0 { // NOTE(rsc): Could take a module pattern. - base.Fatalf("go mod verify: verify takes no arguments") + base.Fatalf("go: verify takes no arguments") } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go index eef5fa5ae8..ed5e9d7f1a 100644 --- a/src/cmd/go/internal/modcmd/why.go +++ b/src/cmd/go/internal/modcmd/why.go @@ -80,13 +80,13 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) { if *whyM { for _, arg := range args { if strings.Contains(arg, "@") { - base.Fatalf("go mod why: module query not allowed") + base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg) } } mods, err := modload.ListModules(ctx, args, 0) if err != nil { - base.Fatalf("go mod why: %v", err) + base.Fatalf("go: %v", err) } byModule := make(map[module.Version][]string) diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 37912ce833..db07624e38 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -263,19 +263,19 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { case "", "upgrade", "patch": // ok default: - base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion) + base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion) } if *getF { - fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -f flag is a no-op when using modules\n") } if *getFix { - fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") + fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op when using modules\n") } if *getM { - base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages") + base.Fatalf("go: -m flag is no longer supported; consider -d to skip building packages") } if *getInsecure { - base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead") + base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead") } // Do not allow any updating of go.mod until we've applied @@ -397,7 +397,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { } } if haveExternalExe { - fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.") + fmt.Fprint(os.Stderr, "go: installing executables with 'go get' in module mode is deprecated.") var altMsg string if modload.HasModRoot() { altMsg = ` @@ -442,7 +442,7 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { for _, arg := range search.CleanPatterns(rawArgs) { q, err := newQuery(arg) if err != nil { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) continue } @@ -457,11 +457,11 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query { // if the argument has no version and either has no slash or refers to an existing file. if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" { if !strings.Contains(q.raw, "/") { - base.Errorf("go get %s: arguments must be package or module paths", q.raw) + base.Errorf("go: %s: arguments must be package or module paths", q.raw) continue } if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() { - base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw) + base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw) continue } } @@ -743,7 +743,7 @@ func (r *resolver) performLocalQueries(ctx context.Context) { if len(match.Pkgs) == 0 { if q.raw == "" || q.raw == "." { - return errSet(fmt.Errorf("no package in current directory")) + return errSet(fmt.Errorf("no package to get in current directory")) } if !q.isWildcard() { modload.MustHaveModRoot() @@ -1342,7 +1342,7 @@ func (r *resolver) applyUpgrades(ctx context.Context, upgrades []pathSet) (chang var tentative []module.Version for _, cs := range upgrades { if cs.err != nil { - base.Errorf("go get: %v", cs.err) + base.Errorf("go: %v", cs.err) continue } @@ -1735,13 +1735,13 @@ func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) { }) for _, c := range sortedChanges { if c.old == "" { - fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new) + fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new) } else if c.new == "none" || c.new == "" { - fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old) + fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old) } else if semver.Compare(c.new, c.old) > 0 { - fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new) } else { - fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new) + fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new) } } @@ -1800,7 +1800,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi if err != nil { var constraint *modload.ConstraintError if !errors.As(err, &constraint) { - base.Errorf("go get: %v", err) + base.Errorf("go: %v", err) return false } @@ -1812,7 +1812,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version}) } for _, c := range constraint.Conflicts { - base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) + base.Errorf("go: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint)) } return false } diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go index 76041906f2..d7341e7813 100644 --- a/src/cmd/go/internal/modget/query.go +++ b/src/cmd/go/internal/modget/query.go @@ -284,21 +284,21 @@ func reportError(q *query, err error) { patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)") if patternRE.MatchString(errStr) { if q.rawVersion == "" { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)") if versionRE.MatchString(errStr) { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) return } } if qs := q.String(); qs != "" { - base.Errorf("go get %s: %s", qs, errStr) + base.Errorf("go: %s: %s", qs, errStr) } else { - base.Errorf("go get: %s", errStr) + base.Errorf("go: %s", errStr) } } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 20c007a03a..40e6b50ed4 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -970,7 +970,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { ld.GoVersion = MainModules.GoVersion() if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 { - ld.errorf("go mod tidy: go.mod file indicates go %s, but maximum supported version is %s\n", ld.GoVersion, LatestGoVersion()) + ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion()) base.ExitIfErrors() } } @@ -1142,7 +1142,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { // If that is not the case, there is a bug in the loading loop above. for _, m := range rs.rootModules { if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version { - ld.errorf("go mod tidy: internal error: a requirement on %v is needed but was not added during package loading\n", m) + ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m) base.ExitIfErrors() } } @@ -1884,7 +1884,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) mg, err := rs.Graph(ctx) if err != nil { - ld.errorf("go mod tidy: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) + ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err) suggestFixes() return } diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 931fdcef8f..11e2c81b9a 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -103,7 +103,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if strings.HasSuffix(file, "_test.go") { // GoFilesPackage is going to assign this to TestGoFiles. // Reject since it won't be part of the build. - base.Fatalf("go run: cannot run *_test.go files (%s)", file) + base.Fatalf("go: cannot run *_test.go files (%s)", file) } } p = load.GoFilesPackage(ctx, pkgOpts, files) @@ -114,26 +114,26 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { var err error pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1]) if err != nil { - base.Fatalf("go run: %v", err) + base.Fatalf("go: %v", err) } } else { pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1]) } if len(pkgs) == 0 { - base.Fatalf("go run: no packages loaded from %s", arg) + base.Fatalf("go: no packages loaded from %s", arg) } if len(pkgs) > 1 { var names []string for _, p := range pkgs { names = append(names, p.ImportPath) } - base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) + base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t")) } p = pkgs[0] i++ } else { - base.Fatalf("go run: no go files listed") + base.Fatalf("go: no go files listed") } cmdArgs := args[i:] load.CheckPackageErrors([]*load.Package{p}) @@ -154,7 +154,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { if !cfg.BuildContext.CgoEnabled { hint = " (cgo is disabled)" } - base.Fatalf("go run: no suitable source files%s", hint) + base.Fatalf("go: no suitable source files%s", hint) } p.Internal.ExeName = src[:len(src)-len(".go")] } else { diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index d65f54f2bd..198afbf4c3 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -656,7 +656,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { b.Init() if cfg.BuildI { - fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") cfg.BuildV = testV deps := make(map[string]bool) diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go index 97a9ef38b9..e0a3e010fa 100644 --- a/src/cmd/go/internal/test/testflag.go +++ b/src/cmd/go/internal/test/testflag.go @@ -384,7 +384,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { if !testC { buildFlag = "-i" } - fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) + fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag) exitWithUsage() } diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go index 95c90ea7c8..4fe4c2baed 100644 --- a/src/cmd/go/internal/tool/tool.go +++ b/src/cmd/go/internal/tool/tool.go @@ -61,7 +61,7 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { switch { case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_': default: - fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName) + fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName) base.SetExitStatus(2) return } @@ -117,14 +117,14 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { func listTools() { f, err := os.Open(base.ToolDir) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err) base.SetExitStatus(2) return } defer f.Close() names, err := f.Readdirnames(-1) if err != nil { - fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err) + fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err) base.SetExitStatus(2) return } diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go index 58cbd32e78..e885933ac3 100644 --- a/src/cmd/go/internal/version/version.go +++ b/src/cmd/go/internal/version/version.go @@ -62,8 +62,14 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) { // a reasonable use case. For example, imagine GOFLAGS=-v to // turn "verbose mode" on for all Go commands, which should not // break "go version". - if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) { - fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n") + var argOnlyFlag string + if !base.InGOFLAGS("-m") && *versionM { + argOnlyFlag = "-m" + } else if !base.InGOFLAGS("-v") && *versionV { + argOnlyFlag = "-v" + } + if argOnlyFlag != "" { + fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag) base.SetExitStatus(2) return } diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go index 1d419dddb9..88b3c570a0 100644 --- a/src/cmd/go/internal/vet/vet.go +++ b/src/cmd/go/internal/vet/vet.go @@ -103,7 +103,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) { continue } if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil { - base.Errorf("go vet %s: no Go files in %s", p.ImportPath, p.Dir) + base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir) continue } if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 { diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go index b5b3c462ff..3551a5997c 100644 --- a/src/cmd/go/internal/vet/vetflag.go +++ b/src/cmd/go/internal/vet/vetflag.go @@ -82,7 +82,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { vetcmd := exec.Command(tool, "-flags") vetcmd.Stdout = out if err := vetcmd.Run(); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err) + fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err) base.SetExitStatus(2) base.Exit() } @@ -92,7 +92,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) { Usage string } if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil { - fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err) + fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err) base.SetExitStatus(2) base.Exit() } diff --git a/src/cmd/go/internal/work/action.go b/src/cmd/go/internal/work/action.go index 69940cb001..6f5ac1364c 100644 --- a/src/cmd/go/internal/work/action.go +++ b/src/cmd/go/internal/work/action.go @@ -294,14 +294,14 @@ func (b *Builder) Init() { } if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil { - fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) + fmt.Fprintf(os.Stderr, "go: %v\n", err) base.SetExitStatus(2) base.Exit() } for _, tag := range cfg.BuildContext.BuildTags { if strings.Contains(tag, ",") { - fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n") + fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n") base.SetExitStatus(2) base.Exit() } diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 3d7c778a7d..e5d7f4a8fd 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -406,7 +406,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { depMode := ModeBuild if cfg.BuildI { depMode = ModeInstall - fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } pkgs = omitTestOnly(pkgsFilter(pkgs)) @@ -425,7 +425,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { strings.HasSuffix(cfg.BuildO, "/") || strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) { if !explicitO { - base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO) + base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO) } a := &Action{Mode: "go build"} for _, p := range pkgs { @@ -440,13 +440,13 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p)) } if len(a.Deps) == 0 { - base.Fatalf("go build: no main packages to build") + base.Fatalf("go: no main packages to build") } b.Do(ctx, a) return } if len(pkgs) > 1 { - base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO) + base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO) } else if len(pkgs) == 0 { base.Fatalf("no packages to build") } @@ -593,7 +593,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { for _, arg := range args { if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) { if cfg.BuildI { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n") } installOutsideModule(ctx, args) return @@ -621,7 +621,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { latestArgs[i] = args[i] + "@latest" } hint := strings.Join(latestArgs, " ") - base.Fatalf("go install: version is required when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) + base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint) } } load.CheckPackageErrors(pkgs) @@ -634,7 +634,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { } } if !allGoroot { - fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n") + fmt.Fprintf(os.Stderr, "go: -i flag is deprecated\n") } } @@ -680,14 +680,14 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag case p.Name != "main" && p.Module != nil: // Non-executables have no target (except the cache) when building with modules. case p.Internal.GobinSubdir: - base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName) + base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set") case p.Internal.CmdlineFiles: - base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName) + base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)") case p.ConflictDir != "": - base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir) + base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir) default: - base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+ - "\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir) + base.Errorf("go: no install location for directory %s outside GOPATH\n"+ + "\tFor more details see: 'go help gopath'", p.Dir) } } } @@ -782,7 +782,7 @@ func installOutsideModule(ctx context.Context, args []string) { pkgOpts := load.PackageOpts{MainOnly: true} pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args) if err != nil { - base.Fatalf("go install: %v", err) + base.Fatalf("go: %v", err) } load.CheckPackageErrors(pkgs) patterns := make([]string, len(args)) diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 7acee3dd55..7aa8dfe55f 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -13,7 +13,6 @@ import ( "cmd/go/internal/modload" "cmd/internal/str" "cmd/internal/sys" - "flag" "fmt" "os" "path/filepath" @@ -33,7 +32,7 @@ func BuildInit() { if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) { p, err := filepath.Abs(cfg.BuildPkgdir) if err != nil { - fmt.Fprintf(os.Stderr, "go %s: evaluating -pkgdir: %v\n", flag.Args()[0], err) + fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err) base.SetExitStatus(2) base.Exit() } @@ -49,14 +48,14 @@ func BuildInit() { value := cfg.Getenv(key) args, err := str.SplitQuotedFields(value) if err != nil { - base.Fatalf("go %s: %s environment variable could not be parsed: %v", flag.Args()[0], key, err) + base.Fatalf("go: %s environment variable could not be parsed: %v", key, err) } if len(args) == 0 { continue } path := args[0] if !filepath.IsAbs(path) && path != filepath.Base(path) { - base.Fatalf("go %s: %s environment variable is relative; must be absolute path: %s\n", flag.Args()[0], key, path) + base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path) } } } @@ -66,7 +65,7 @@ func instrumentInit() { return } if cfg.BuildRace && cfg.BuildMSan { - fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n") base.SetExitStatus(2) base.Exit() } @@ -77,7 +76,7 @@ func instrumentInit() { } if cfg.BuildRace { if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) { - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n") base.SetExitStatus(2) base.Exit() } @@ -95,9 +94,9 @@ func instrumentInit() { if !cfg.BuildContext.CgoEnabled { if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag) } else { - fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag) + fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag) } base.SetExitStatus(2) diff --git a/src/cmd/go/testdata/script/build_i_deprecate.txt b/src/cmd/go/testdata/script/build_i_deprecate.txt index 71356e5321..5c17995669 100644 --- a/src/cmd/go/testdata/script/build_i_deprecate.txt +++ b/src/cmd/go/testdata/script/build_i_deprecate.txt @@ -2,13 +2,13 @@ # TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test. go build -n -i -stderr '^go build: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go install -n -i -stderr '^go install: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' go test -n -i -stderr '^go test: -i flag is deprecated$' +stderr '^go: -i flag is deprecated$' # 'go clean -i' should not print a deprecation warning. diff --git a/src/cmd/go/testdata/script/env_unset.txt b/src/cmd/go/testdata/script/env_unset.txt index 4e0f249509..22bc845c37 100644 --- a/src/cmd/go/testdata/script/env_unset.txt +++ b/src/cmd/go/testdata/script/env_unset.txt @@ -12,13 +12,13 @@ stderr '^go(\.exe)?: unknown GOEXPERIMENT badexp$' go env -u GOEXPERIMENT ! go env -stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair bados/badarch$' ! go env -u GOOS -stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$' +stderr '^go: unsupported GOOS/GOARCH pair \w+/badarch$' ! go env -u GOARCH -stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$' +stderr '^go: unsupported GOOS/GOARCH pair bados/\w+$' go env -u GOOS GOARCH diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt index b5e9739167..132947c623 100644 --- a/src/cmd/go/testdata/script/env_write.txt +++ b/src/cmd/go/testdata/script/env_write.txt @@ -30,9 +30,9 @@ go env # checking errors ! go env -w -stderr 'go env -w: no KEY=VALUE arguments given' +stderr 'go: no KEY=VALUE arguments given' ! go env -u -stderr 'go env -u: no arguments given' +stderr 'go: ''go env -u'' requires an argument' # go env -w changes default setting env root= @@ -111,7 +111,7 @@ stderr 'GOPATH entry is relative; must be absolute path' # go env -w rejects invalid GOTMPDIR values ! go env -w GOTMPDIR=x -stderr 'go env -w: GOTMPDIR must be an absolute path' +stderr 'go: GOTMPDIR must be an absolute path' # go env -w should accept absolute GOTMPDIR value # and should not create it @@ -134,24 +134,24 @@ stdout ^$ go env -w CC=clang [!windows] ! go env -w CC=./clang [!windows] ! go env -w CC=bin/clang -[!windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[!windows] stderr 'go: CC entry is relative; must be absolute path' [windows] go env -w CC=$WORK\bin\clang [windows] ! go env -w CC=.\clang [windows] ! go env -w CC=bin\clang -[windows] stderr 'go env -w: CC entry is relative; must be absolute path' +[windows] stderr 'go: CC entry is relative; must be absolute path' # go env -w rejects relative CXX values [!windows] go env -w CC=/usr/bin/cpp go env -w CXX=cpp [!windows] ! go env -w CXX=./cpp [!windows] ! go env -w CXX=bin/cpp -[!windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[!windows] stderr 'go: CXX entry is relative; must be absolute path' [windows] go env -w CXX=$WORK\bin\cpp [windows] ! go env -w CXX=.\cpp [windows] ! go env -w CXX=bin\cpp -[windows] stderr 'go env -w: CXX entry is relative; must be absolute path' +[windows] stderr 'go: CXX entry is relative; must be absolute path' # go env -w/-u checks validity of GOOS/ARCH combinations env GOOS= @@ -176,9 +176,9 @@ stderr 'unsupported GOOS/GOARCH.*windows/mips$' # go env -w should reject relative paths in GOMODCACHE environment. ! go env -w GOMODCACHE=~/test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "~/test"' ! go env -w GOMODCACHE=./test -stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"' +stderr 'go: GOMODCACHE entry is relative; must be absolute path: "./test"' # go env -w checks validity of GOEXPERIMENT env GOEXPERIMENT= diff --git a/src/cmd/go/testdata/script/get_go_file.txt b/src/cmd/go/testdata/script/get_go_file.txt index bed8720987..f6d0de4d06 100644 --- a/src/cmd/go/testdata/script/get_go_file.txt +++ b/src/cmd/go/testdata/script/get_go_file.txt @@ -9,15 +9,15 @@ go get -d test # argument has .go suffix, is a file and exists ! go get -d test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get -d test_missing.go -stderr 'go get test_missing.go: arguments must be package or module paths' +stderr 'go: test_missing.go: arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get -d test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get -d test/test_missing.go @@ -27,19 +27,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get -d test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get -d test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get -d test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory diff --git a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt index 2517664dd0..00bf32fc78 100644 --- a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt +++ b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt @@ -3,11 +3,11 @@ env GO111MODULE=off # GOPATH: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' # Modules: Set up env GO111MODULE=on # Modules: Fetch with insecure, should error ! go get -insecure test -stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead' +stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead' diff --git a/src/cmd/go/testdata/script/gopath_install.txt b/src/cmd/go/testdata/script/gopath_install.txt index 4b42fc593f..6c572eae61 100644 --- a/src/cmd/go/testdata/script/gopath_install.txt +++ b/src/cmd/go/testdata/script/gopath_install.txt @@ -26,7 +26,7 @@ cd .. env GOPATH= # reset to default ($HOME/go, which does not exist) env GOBIN= ! go install go-cmd-test/helloworld.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' # With $GOBIN set, should install there. env GOBIN=$WORK/bin1 diff --git a/src/cmd/go/testdata/script/gopath_local.txt b/src/cmd/go/testdata/script/gopath_local.txt index 7ee1f83471..54beaca5e8 100644 --- a/src/cmd/go/testdata/script/gopath_local.txt +++ b/src/cmd/go/testdata/script/gopath_local.txt @@ -22,7 +22,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/local/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' [windows] stop # Windows does not allow the ridiculous directory name we're about to use. @@ -58,7 +58,7 @@ stdout '^sub\.Hello' # Explicit source files listed on the command line should not install without # GOBIN set, since individual source files aren't part of the containing GOPATH. ! go install testdata/$BAD_DIR_NAME/easy.go -stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$' +stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$' -- testdata/local/easy.go -- package main diff --git a/src/cmd/go/testdata/script/govcs.txt b/src/cmd/go/testdata/script/govcs.txt index 4180d7da6a..e8dd791267 100644 --- a/src/cmd/go/testdata/script/govcs.txt +++ b/src/cmd/go/testdata/script/govcs.txt @@ -5,40 +5,40 @@ env GOPROXY=direct # GOVCS stops go get env GOVCS='*:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOPRIVATE='github.com/google' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # public pattern works env GOPRIVATE='github.com/google' env GOVCS='public:all,private:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$' # private pattern works env GOPRIVATE='hubgit.com/google' env GOVCS='private:all,public:none' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # other patterns work (for more patterns, see TestGOVCS) env GOPRIVATE= env GOVCS='github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' env GOVCS='github.com/google/go-cmp/inner:git,github.com:svn|hg' ! go get github.com/google/go-cmp -stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' +stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$' # bad patterns are reported (for more bad patterns, see TestGOVCSErrors) env GOVCS='git' ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' +stderr '^go: github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$' env GOVCS=github.com:hg,github.com:git ! go get github.com/google/go-cmp -stderr '^go get github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' +stderr '^go: github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$' # bad GOVCS patterns do not stop commands that do not need to check VCS go list @@ -50,19 +50,19 @@ env GOPROXY=direct env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.svn/hello -stderr '^go get rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$' # fossil is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.fossil/hello -stderr '^go get rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$' # bzr is disallowed by default env GOPRIVATE= env GOVCS= ! go get rsc.io/nonexist.bzr/hello -stderr '^go get rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$' # git is OK by default env GOVCS= @@ -77,12 +77,12 @@ env GONOSUMDB='*' # git can be disallowed env GOVCS=public:hg ! go get rsc.io/nonexist.git/hello -stderr '^go get rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$' # hg can be disallowed env GOVCS=public:git ! go get rsc.io/nonexist.hg/hello -stderr '^go get rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' +stderr '^go: rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$' # Repeat in GOPATH mode. Error texts slightly different. diff --git a/src/cmd/go/testdata/script/list_shadow.txt b/src/cmd/go/testdata/script/list_shadow.txt index 7b24d9367a..660508de9f 100644 --- a/src/cmd/go/testdata/script/list_shadow.txt +++ b/src/cmd/go/testdata/script/list_shadow.txt @@ -15,7 +15,7 @@ stdout '^\(.*gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo\) \('$WORK # The error for go install should mention the conflicting directory. ! go install -n ./shadow/root2/src/foo -stderr 'go install: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' +stderr 'go: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo' -- shadow/root1/src/foo/foo.go -- package foo diff --git a/src/cmd/go/testdata/script/mod_bad_domain.txt b/src/cmd/go/testdata/script/mod_bad_domain.txt index 7a270d0f07..ed6a8c656d 100644 --- a/src/cmd/go/testdata/script/mod_bad_domain.txt +++ b/src/cmd/go/testdata/script/mod_bad_domain.txt @@ -2,7 +2,7 @@ env GO111MODULE=on # explicit get should report errors about bad names ! go get appengine -stderr '^go get: malformed module path "appengine": missing dot in first path element$' +stderr '^go: malformed module path "appengine": missing dot in first path element$' ! go get x/y.z stderr 'malformed module path "x/y.z": missing dot in first path element' diff --git a/src/cmd/go/testdata/script/mod_dot.txt b/src/cmd/go/testdata/script/mod_dot.txt index ca8d5c6cc2..cb60e988b6 100644 --- a/src/cmd/go/testdata/script/mod_dot.txt +++ b/src/cmd/go/testdata/script/mod_dot.txt @@ -5,11 +5,11 @@ env GO111MODULE=on # to resolve an external module. cd dir ! go get -stderr '^go get: no package in current directory$' +stderr '^go: no package to get in current directory$' ! go get . -stderr '^go get \.: no package in current directory$' +stderr '^go: .: no package to get in current directory$' ! go get ./subdir -stderr '^go get: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' +stderr '^go: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$' ! go list ! stderr 'cannot find module providing package' stderr '^no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$' diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt index c2b72b2a02..89e58a2cfd 100644 --- a/src/cmd/go/testdata/script/mod_download.txt +++ b/src/cmd/go/testdata/script/mod_download.txt @@ -93,19 +93,19 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip # download reports errors encountered when locating modules ! go mod download bad/path -stderr '^go mod download: module bad/path: not a known dependency$' +stderr '^go: module bad/path: not a known dependency$' ! go mod download bad/path@latest -stderr '^go mod download: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' +stderr '^go: bad/path@latest: malformed module path "bad/path": missing dot in first path element$' ! go mod download rsc.io/quote@v1.999.999 -stderr '^go mod download: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' +stderr '^go: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$' ! go mod download -json bad/path stdout '^\t"Error": "module bad/path: not a known dependency"' # download main module produces a warning or error go mod download m -stderr '^go mod download: skipping argument m that resolves to the main module\n' +stderr '^go: skipping download of m that resolves to the main module\n' ! go mod download m@latest -stderr '^go mod download: m@latest: malformed module path "m": missing dot in first path element$' +stderr '^go: m@latest: malformed module path "m": missing dot in first path element$' # download without arguments updates go.mod and go.sum after loading the # build list, but does not save sums for downloaded zips. diff --git a/src/cmd/go/testdata/script/mod_edit.txt b/src/cmd/go/testdata/script/mod_edit.txt index 5aa5ca1ffc..ebc032a73c 100644 --- a/src/cmd/go/testdata/script/mod_edit.txt +++ b/src/cmd/go/testdata/script/mod_edit.txt @@ -23,18 +23,18 @@ cmpenv go.mod $WORK/go.mod.edit2 # -exclude and -retract reject invalid versions. ! go mod edit -exclude=example.com/m@bad -stderr '^go mod: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -retract=bad -stderr '^go mod: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' +stderr '^go: -retract=bad: version "bad" invalid: must be of the form v1.2.3$' ! go mod edit -exclude=example.com/m@v2.0.0 -stderr '^go mod: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' +stderr '^go: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$' ! go mod edit -exclude=example.com/m/v2@v1.0.0 -stderr '^go mod: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' +stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$' ! go mod edit -exclude=gopkg.in/example.v1@v2.0.0 -stderr '^go mod: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' +stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$' cmpenv go.mod $WORK/go.mod.edit2 diff --git a/src/cmd/go/testdata/script/mod_get_changes.txt b/src/cmd/go/testdata/script/mod_get_changes.txt index 3287b2a609..2829111fe5 100644 --- a/src/cmd/go/testdata/script/mod_get_changes.txt +++ b/src/cmd/go/testdata/script/mod_get_changes.txt @@ -4,8 +4,8 @@ go list -m all ! stdout golang.org/x/text go get -d rsc.io/quote@v1.5.2 -stderr '^go get: added rsc.io/quote v1.5.2$' -stderr '^go get: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' +stderr '^go: added rsc.io/quote v1.5.2$' +stderr '^go: upgraded rsc.io/sampler v1.0.0 => v1.3.0$' ! stderr '^go get.*golang.org/x/text' go list -m all stdout golang.org/x/text @@ -15,8 +15,8 @@ cmp go.mod go.mod.upgrade # and for changed explicit dependencies. 'go get' does not print messages # for changed indirect dependencies. go get -d rsc.io/sampler@none -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.3.0$' -stderr '^go get: removed rsc.io/sampler v1.3.0$' +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.3.0$' +stderr '^go: removed rsc.io/sampler v1.3.0$' ! stderr '^go get.*golang.org/x/text' cmp go.mod go.mod.downgrade @@ -24,8 +24,8 @@ cmp go.mod go.mod.downgrade # for explicit dependencies removed as a consequence. cp go.mod.usequote go.mod go get -d rsc.io/quote@v1.5.1 -stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.5.1$' -stderr '^go get: removed usequote v0.0.0$' +stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.5.1$' +stderr '^go: removed usequote v0.0.0$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt index 63cd27a42d..e8142afee9 100644 --- a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt +++ b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt @@ -4,7 +4,7 @@ env GO111MODULE=on # 'go get' outside a module with an executable prints a deprecation message. go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' +stderr '^go: installing executables with ''go get'' in module mode is deprecated.$' stderr 'Use ''go install pkg@version'' instead.' cp go.mod.orig go.mod @@ -18,7 +18,7 @@ cp go.mod.orig go.mod # 'go get' inside a module with an executable prints a different # deprecation message. go get example.com/cmd/a -stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$' +stderr '^go: installing executables with ''go get'' in module mode is deprecated.$' stderr 'To adjust and download dependencies of the current module, use ''go get -d''' cp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_get_downgrade.txt b/src/cmd/go/testdata/script/mod_get_downgrade.txt index c26c5e1c21..685bde7efd 100644 --- a/src/cmd/go/testdata/script/mod_get_downgrade.txt +++ b/src/cmd/go/testdata/script/mod_get_downgrade.txt @@ -20,8 +20,8 @@ stdout 'rsc.io/quote v1.5.1' stdout 'rsc.io/sampler v1.3.0' ! go get -d rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none -stderr -count=1 '^go get:' -stderr '^go get: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' +! stderr add|remove|upgrad|downgrad +stderr '^go: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$' go list -m all stdout 'rsc.io/quote v1.5.1' diff --git a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt index 5b768faeb1..2068cae755 100644 --- a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt +++ b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt @@ -5,7 +5,7 @@ cp go.mod go.mod.orig # rather than a "matched no packages" warning. ! go get -d example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/... -stderr '^go get: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' +stderr '^go: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$' ! stderr 'matched no packages' cmp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_get_go_file.txt b/src/cmd/go/testdata/script/mod_get_go_file.txt index 0c7b5dc11c..35a77a9d83 100644 --- a/src/cmd/go/testdata/script/mod_get_go_file.txt +++ b/src/cmd/go/testdata/script/mod_get_go_file.txt @@ -17,7 +17,7 @@ env GO111MODULE=on # argument has .go suffix, is a file and exists ! go get test.go -stderr 'go get test.go: arguments must be package or module paths' +stderr 'go: test.go: arguments must be package or module paths' # argument has .go suffix, doesn't exist and has no slashes ! go get test_missing.go @@ -25,7 +25,7 @@ stderr 'arguments must be package or module paths' # argument has .go suffix, is a file and exists in sub-directory ! go get test/test.go -stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments' +stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments' # argument has .go suffix, doesn't exist and has slashes ! go get test/test_missing.go @@ -35,19 +35,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a # argument has .go suffix, is a symlink and exists [symlink] symlink test_sym.go -> test.go [symlink] ! go get test_sym.go -[symlink] stderr 'go get test_sym.go: arguments must be package or module paths' +[symlink] stderr 'go: test_sym.go: arguments must be package or module paths' [symlink] rm test_sym.go # argument has .go suffix, is a symlink and exists in sub-directory [symlink] symlink test/test_sym.go -> test.go [symlink] ! go get test/test_sym.go -[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments' +[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments' [symlink] rm test_sym.go # argument has .go suffix, is a directory and exists mkdir test_dir.go ! go get test_dir.go -stderr 'go get test_dir.go: arguments must be package or module paths' +stderr 'go: test_dir.go: arguments must be package or module paths' rm test_dir.go # argument has .go suffix, is a directory and exists in sub-directory diff --git a/src/cmd/go/testdata/script/mod_get_main.txt b/src/cmd/go/testdata/script/mod_get_main.txt index 50b2fee9ae..5c9b762543 100644 --- a/src/cmd/go/testdata/script/mod_get_main.txt +++ b/src/cmd/go/testdata/script/mod_get_main.txt @@ -3,13 +3,13 @@ cp go.mod.orig go.mod # relative and absolute paths must be within the main module. ! go get -d .. -stderr '^go get: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +stderr '^go: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' ! go get -d $WORK -stderr '^go get: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +stderr '^go: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' ! go get -d ../... -stderr '^go get: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +stderr '^go: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' ! go get -d $WORK/... -stderr '^go get: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' +stderr '^go: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$' # @patch and @latest within the main module refer to the current version. # The main module won't be upgraded, but missing dependencies will be added. @@ -31,15 +31,15 @@ grep 'rsc.io/quote v1.5.1' go.mod # The main module cannot be updated to a specific version. ! go get -d rsc.io@v0.1.0 -stderr '^go get: can''t request version "v0.1.0" of the main module \(rsc.io\)$' +stderr '^go: can''t request version "v0.1.0" of the main module \(rsc.io\)$' # A package in the main module can't be upgraded either. ! go get -d rsc.io/x@v0.1.0 -stderr '^go get: package rsc.io/x is in the main module, so can''t request version v0.1.0$' +stderr '^go: package rsc.io/x is in the main module, so can''t request version v0.1.0$' # Nor can a pattern matching packages in the main module. ! go get -d rsc.io/x/...@latest -stderr '^go get: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' +stderr '^go: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$' -- go.mod.orig -- module rsc.io diff --git a/src/cmd/go/testdata/script/mod_get_newcycle.txt b/src/cmd/go/testdata/script/mod_get_newcycle.txt index f71620c1bc..18dc650361 100644 --- a/src/cmd/go/testdata/script/mod_get_newcycle.txt +++ b/src/cmd/go/testdata/script/mod_get_newcycle.txt @@ -11,4 +11,4 @@ go mod init m cmp stderr stderr-expected -- stderr-expected -- -go get: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 +go: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0 diff --git a/src/cmd/go/testdata/script/mod_get_nopkgs.txt b/src/cmd/go/testdata/script/mod_get_nopkgs.txt index 078e71a041..2711f93498 100644 --- a/src/cmd/go/testdata/script/mod_get_nopkgs.txt +++ b/src/cmd/go/testdata/script/mod_get_nopkgs.txt @@ -16,7 +16,7 @@ go get -d example.net/emptysubdir/... # control case ! go get -d example.net/emptysubdir/subdir/... ! stderr 'matched no packages' -stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' +stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z' # It doesn't make sense to 'go get' a path in the standard library, # since the standard library necessarily can't have unresolved imports. @@ -27,7 +27,7 @@ stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/empt # which isn't ideal either. ! go get -d builtin/... # in GOROOT/src, but contains no packages -stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$' +stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$' -- go.mod -- module example.net/emptysubdir diff --git a/src/cmd/go/testdata/script/mod_get_patch.txt b/src/cmd/go/testdata/script/mod_get_patch.txt index 053ef62147..5957a360b0 100644 --- a/src/cmd/go/testdata/script/mod_get_patch.txt +++ b/src/cmd/go/testdata/script/mod_get_patch.txt @@ -8,7 +8,7 @@ cp go.mod go.mod.orig # at the start of 'go get', not the version after applying other changes. ! go get -d example.net/a@v0.2.0 example.net/b@patch -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -19,7 +19,7 @@ cmp go.mod go.mod.orig # TODO(#42360): Reconsider the change in defaults. ! go get -d -u=patch example.net/a@v0.2.0 example.net/b -stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' +stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$' cmp go.mod go.mod.orig @@ -38,7 +38,7 @@ stdout '^example.net/b v0.2.1 ' cp go.mod.orig go.mod ! go get -u=patch all -stderr '^go get: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' +stderr '^go: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$' cmp go.mod go.mod.orig diff --git a/src/cmd/go/testdata/script/mod_get_patchcycle.txt b/src/cmd/go/testdata/script/mod_get_patchcycle.txt index d1db56f935..6600109d2d 100644 --- a/src/cmd/go/testdata/script/mod_get_patchcycle.txt +++ b/src/cmd/go/testdata/script/mod_get_patchcycle.txt @@ -6,7 +6,7 @@ # (It used to print v0.1.1 but then silently upgrade to v0.2.0.) ! go get example.net/a@patch -stderr '^go get: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. +stderr '^go: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice. -- go.mod -- module example diff --git a/src/cmd/go/testdata/script/mod_get_patchmod.txt b/src/cmd/go/testdata/script/mod_get_patchmod.txt index e39d13a0f4..bc1859edc2 100644 --- a/src/cmd/go/testdata/script/mod_get_patchmod.txt +++ b/src/cmd/go/testdata/script/mod_get_patchmod.txt @@ -16,7 +16,7 @@ cp go.mod go.mod.orig # not upgraded to the latest patch of the new transitive dependency. ! go get -d example.net/pkgremoved@patch example.net/other@v0.1.0 -stderr '^go get: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' +stderr '^go: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$' cmp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_get_patterns.txt b/src/cmd/go/testdata/script/mod_get_patterns.txt index aee4374dc8..3b5ab43339 100644 --- a/src/cmd/go/testdata/script/mod_get_patterns.txt +++ b/src/cmd/go/testdata/script/mod_get_patterns.txt @@ -10,11 +10,11 @@ grep 'require rsc.io/quote' go.mod cp go.mod.orig go.mod ! go get -d rsc.io/quote/x... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...' ! grep 'require rsc.io/quote' go.mod ! go get -d rsc.io/quote/x/... -stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' +stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...' ! grep 'require rsc.io/quote' go.mod # If a pattern matches no packages within a module, the module should not diff --git a/src/cmd/go/testdata/script/mod_get_pkgtags.txt b/src/cmd/go/testdata/script/mod_get_pkgtags.txt index 0c79ec71b7..2e2ab72032 100644 --- a/src/cmd/go/testdata/script/mod_get_pkgtags.txt +++ b/src/cmd/go/testdata/script/mod_get_pkgtags.txt @@ -59,7 +59,7 @@ stderr '^example.net/testonly tested by\n\texample.net/testonly\.test imports\n\ # but fail for a non-package subdirectory of a module. ! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' go get -d example.net/missing@v0.1.0 @@ -68,7 +68,7 @@ go get -d example.net/missing@v0.1.0 # module is already present in the build list. ! go get -d example.net/missing/subdir@v0.1.0 -stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' +stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_get_private_vcs.txt b/src/cmd/go/testdata/script/mod_get_private_vcs.txt index 75c776a7fa..c8862f42f9 100644 --- a/src/cmd/go/testdata/script/mod_get_private_vcs.txt +++ b/src/cmd/go/testdata/script/mod_get_private_vcs.txt @@ -13,7 +13,7 @@ stderr 'If this is a private repository, see https://golang.org/doc/faq#git_http # Fetching a nonexistent commit should return an "unknown revision" # error message. ! go get github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b -stderr '^go get: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' +stderr '^go: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$' ! stdout . ! go get github.com/golang/nonexist@master diff --git a/src/cmd/go/testdata/script/mod_get_replaced.txt b/src/cmd/go/testdata/script/mod_get_replaced.txt index d97f3f1a40..ab21bd57fa 100644 --- a/src/cmd/go/testdata/script/mod_get_replaced.txt +++ b/src/cmd/go/testdata/script/mod_get_replaced.txt @@ -82,7 +82,7 @@ cp go.mod.orig go.mod ! go list example stderr '^package example is not in GOROOT \(.*\)$' ! go get -d example -stderr '^go get: malformed module path "example": missing dot in first path element$' +stderr '^go: malformed module path "example": missing dot in first path element$' go mod edit -replace example@v0.1.0=./example diff --git a/src/cmd/go/testdata/script/mod_get_split.txt b/src/cmd/go/testdata/script/mod_get_split.txt index f4e7661f9b..2fb88ab2da 100644 --- a/src/cmd/go/testdata/script/mod_get_split.txt +++ b/src/cmd/go/testdata/script/mod_get_split.txt @@ -55,7 +55,7 @@ stderr '^example.net/split/nested: ambiguous import: found package example.net/s # TODO(#27899): Should we instead upgrade or downgrade to an arbirary version? ! go get -d example.net/split/nested/...@v0.1.0 -stderr '^go get: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' +stderr '^go: example.net/split/nested/\.\.\.@v0.1.0 matches packages in example.net/split@v0.2.0 but not example.net/split@v0.1.0: specify a different version for module example.net/split$' cmp go.mod go.mod.orig diff --git a/src/cmd/go/testdata/script/mod_get_svn.txt b/src/cmd/go/testdata/script/mod_get_svn.txt index 3817fce9b6..4d6b94ae5b 100644 --- a/src/cmd/go/testdata/script/mod_get_svn.txt +++ b/src/cmd/go/testdata/script/mod_get_svn.txt @@ -27,7 +27,7 @@ exists $GOPATH/bin/hello.svn$GOEXE # reasonable message instead of a panic. ! go get -d vcs-test.golang.org/svn/nonexistent.svn ! stderr panic -stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"' +stderr 'go: vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"' -- go.mod -- module golang/go/issues/28943/main diff --git a/src/cmd/go/testdata/script/mod_get_wild.txt b/src/cmd/go/testdata/script/mod_get_wild.txt index 78c645c6b9..b88f268a1d 100644 --- a/src/cmd/go/testdata/script/mod_get_wild.txt +++ b/src/cmd/go/testdata/script/mod_get_wild.txt @@ -12,7 +12,7 @@ stdout '^example.net/a v0.1.0 ' # from attempting to resolve a new module whose path is a prefix of the pattern. ! go get -d -u=patch example.../b@upgrade -stderr '^go get: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' +stderr '^go: no modules to query for example\.\.\./b@upgrade because first path element contains a wildcard$' # Patching . causes a patch to example.net/a, which introduces a new match diff --git a/src/cmd/go/testdata/script/mod_getmode_vendor.txt b/src/cmd/go/testdata/script/mod_getmode_vendor.txt index 00070c03b5..a4e23ac9d0 100644 --- a/src/cmd/go/testdata/script/mod_getmode_vendor.txt +++ b/src/cmd/go/testdata/script/mod_getmode_vendor.txt @@ -11,16 +11,16 @@ stdout '^rsc.io/quote v1.5.1 .*vendor[\\/]rsc.io[\\/]quote$' stdout '^golang.org/x/text v0.0.0.* .*vendor[\\/]golang.org[\\/]x[\\/]text[\\/]language$' ! go list -mod=vendor -m rsc.io/quote@latest -stderr 'go list -m: rsc.io/quote@latest: cannot query module due to -mod=vendor' +stderr 'go: rsc.io/quote@latest: cannot query module due to -mod=vendor' ! go get -mod=vendor -u stderr 'flag provided but not defined: -mod' # Since we don't have a complete module graph, 'go list -m' queries # that require the complete graph should fail with a useful error. ! go list -mod=vendor -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -mod=vendor -m ... -stderr 'go list -m: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t match module patterns using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' -- go.mod -- module x diff --git a/src/cmd/go/testdata/script/mod_gonoproxy.txt b/src/cmd/go/testdata/script/mod_gonoproxy.txt index 204786969f..190940030c 100644 --- a/src/cmd/go/testdata/script/mod_gonoproxy.txt +++ b/src/cmd/go/testdata/script/mod_gonoproxy.txt @@ -27,13 +27,13 @@ stdout '^golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c$' # When GOPROXY is not empty but contains no entries, an error should be reported. env GOPROXY=',' ! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' +stderr '^go: golang.org/x/text: GOPROXY list is not the empty string, but contains no entries$' # When GOPROXY=off, fetching modules not matched by GONOPROXY fails. env GONOPROXY=*/fortune env GOPROXY=off ! go get -d golang.org/x/text -stderr '^go get golang.org/x/text: module lookup disabled by GOPROXY=off$' +stderr '^go: golang.org/x/text: module lookup disabled by GOPROXY=off$' # GONOPROXY bypasses proxy [!net] skip diff --git a/src/cmd/go/testdata/script/mod_install_pkg_version.txt b/src/cmd/go/testdata/script/mod_install_pkg_version.txt index fd02392af1..1ee68e7ad8 100644 --- a/src/cmd/go/testdata/script/mod_install_pkg_version.txt +++ b/src/cmd/go/testdata/script/mod_install_pkg_version.txt @@ -16,7 +16,7 @@ env GO111MODULE=auto cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go install example.com/cmd/a@latest cmp go.mod go.mod.orig exists $GOPATH/bin/a$GOEXE @@ -81,15 +81,15 @@ env GO111MODULE=auto # 'go install pkg@version' reports errors for meta packages, std packages, # and directories. ! go install std@v1.0.0 -stderr '^go install: std@v1.0.0: argument must be a package path, not a meta-package$' +stderr '^go: std@v1.0.0: argument must be a package path, not a meta-package$' ! go install fmt@v1.0.0 -stderr '^go install: fmt@v1.0.0: argument must not be a package in the standard library$' +stderr '^go: fmt@v1.0.0: argument must not be a package in the standard library$' ! go install example.com//cmd/a@v1.0.0 -stderr '^go install: example.com//cmd/a@v1.0.0: argument must be a clean package path$' +stderr '^go: example.com//cmd/a@v1.0.0: argument must be a clean package path$' ! go install example.com/cmd/a@v1.0.0 ./x@v1.0.0 -stderr '^go install: ./x@v1.0.0: argument must be a package path, not a relative path$' +stderr '^go: ./x@v1.0.0: argument must be a package path, not a relative path$' ! go install example.com/cmd/a@v1.0.0 $GOPATH/src/x@v1.0.0 -stderr '^go install: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' +stderr '^go: '$WORK'[/\\]gopath/src/x@v1.0.0: argument must be a package path, not an absolute path$' ! go install example.com/cmd/a@v1.0.0 cmd/...@v1.0.0 stderr '^package cmd/go not provided by module example.com/cmd@v1.0.0$' @@ -106,7 +106,7 @@ stdout '^example.com/cmd v1.0.0$' env GO111MODULE=auto ! go install example.com/cmd/a@v1.0.0 example.com/cmd/b@latest -stderr '^go install: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' +stderr '^go: example.com/cmd/b@latest: all arguments must have the same version \(@v1.0.0\)$' # 'go install pkg@version' should report an error if the arguments are in @@ -137,7 +137,7 @@ rm $GOPATH/bin # If a wildcard matches no packages, we should see a warning. ! go install example.com/cmd/nomatch...@v1.0.0 -stderr '^go install: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' +stderr '^go: example.com/cmd/nomatch\.\.\.@v1.0.0: module example.com/cmd@v1.0.0 found, but does not contain packages matching example.com/cmd/nomatch\.\.\.$' go install example.com/cmd/a@v1.0.0 example.com/cmd/nomatch...@v1.0.0 stderr '^go: warning: "example.com/cmd/nomatch\.\.\." matched no packages$' @@ -159,7 +159,7 @@ cmp stderr exclude-err # 'go install pkg@version' should report an error if the module requires a # higher version of itself. ! go install example.com/cmd/a@v1.0.0-newerself -stderr '^go install: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' +stderr '^go: example.com/cmd/a@v1.0.0-newerself: version constraints conflict:\n\texample.com/cmd@v1.0.0-newerself requires example.com/cmd@v1.0.0, but example.com/cmd@v1.0.0-newerself is requested$' # 'go install pkg@version' will only match a retracted version if it's @@ -192,12 +192,12 @@ package main func main() {} -- replace-err -- -go install: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go install: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/src/cmd/go/testdata/script/mod_invalid_path.txt b/src/cmd/go/testdata/script/mod_invalid_path.txt index 333a3ffa35..766e9c0909 100644 --- a/src/cmd/go/testdata/script/mod_invalid_path.txt +++ b/src/cmd/go/testdata/script/mod_invalid_path.txt @@ -29,7 +29,7 @@ stdout '^example.com/dotname/.dot$' go list ./use stdout '^example.com/dotname/use$' ! go list -m example.com/dotname/.dot@latest -stderr '^go list -m: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' +stderr '^go: example.com/dotname/.dot@latest: malformed module path "example.com/dotname/.dot": leading dot in path element$' go get -d example.com/dotname/.dot go get -d example.com/dotname/use go mod tidy diff --git a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt index 51dbf93688..6a29eb8ce0 100644 --- a/src/cmd/go/testdata/script/mod_invalid_path_plus.txt +++ b/src/cmd/go/testdata/script/mod_invalid_path_plus.txt @@ -9,7 +9,7 @@ go list example.net/cmd/x++ # 'go list -m' rejects module paths with pluses. ! go list -versions -m 'example.net/bad++' -stderr '^go list -m: malformed module path "example.net/bad\+\+": invalid char ''\+''$' +stderr '^go: malformed module path "example.net/bad\+\+": invalid char ''\+''$' # 'go get' accepts package paths with pluses. cp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt index 6846a792a5..31b25f757e 100644 --- a/src/cmd/go/testdata/script/mod_invalid_version.txt +++ b/src/cmd/go/testdata/script/mod_invalid_version.txt @@ -19,7 +19,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "14c0d48ead0c" invalid: must be of the form v1.2.3' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.1.1-0.20170915032832-14c0d48ead0c' @@ -30,7 +30,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text/unicode@v0.0.0-20170915032832-14c0d48ead0c: invalid version: missing golang.org/x/text/unicode/go.mod at revision 14c0d48ead0c' @@ -47,7 +47,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v2.1.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' +stderr 'go: example.com@v0.0.0 \(replaced by \./\.\.\): parsing ..[/\\]go.mod: '$WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' cd .. ! go list -m golang.org/x/text stderr $WORK'[/\\]gopath[/\\]src[/\\]go.mod:5: require golang.org/x/text: version "v2.1.1-0.20170915032832-14c0d48ead0c" invalid: should be v0 or v1, not v2' @@ -57,7 +57,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0 cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)' @@ -67,7 +67,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)' @@ -77,7 +77,7 @@ 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 list -m: 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\)' +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 \(expected 20170915032832\)' @@ -87,7 +87,7 @@ stderr 'golang.org/x/text@v0.1.1-0.20190915032832-14c0d48ead0c: invalid pseudo-v 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 list -m: 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\)' +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' @@ -97,7 +97,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.999.999-0.20170915032832-14c0d48ead0c: invalid pseudo-version: preceding tag \(v1.999.998\) not found' @@ -109,7 +109,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v1.0.0-20170915032832-14c0d48ead0c: invalid pseudo-version: major version without preceding tag must be v0, not v1' @@ -120,7 +120,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' @@ -130,7 +130,7 @@ stderr 'golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-v go mod edit -replace golang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c=golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.0.0-0.20170915032832-14c0d48ead0c: invalid pseudo-version: version before v0.0.0 would have negative patch number' cd .. go list -m golang.org/x/text stdout 'golang.org/x/text v0.0.0-0.20170915032832-14c0d48ead0c => golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c' @@ -153,7 +153,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20170915032832-14c0d48ead0c: invalid pseudo-version: revision 14c0d48ead0c is not a descendent of preceding tag \(v0.2.0\)' @@ -163,7 +163,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.2.1-0.20171213102548-c4d099d611ac: invalid pseudo-version: tag \(v0.2.0\) found on revision c4d099d611ac is already canonical, so should not be replaced with a pseudo-version derived from that tag' @@ -173,7 +173,7 @@ cp go.mod.orig go.mod go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c+incompatible cd outside ! go list -m golang.org/x/text -stderr 'go list -m: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' +stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' cd .. ! go list -m golang.org/x/text stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0c\+incompatible: invalid version: \+incompatible suffix not allowed: major version v0 is compatible' @@ -194,7 +194,7 @@ cp go.mod.orig go.mod go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible cd outside ! go list -m github.com/pierrec/lz4 -stderr 'go list -m: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' +stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' cd .. ! go list -m github.com/pierrec/lz4 stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required' @@ -222,7 +222,7 @@ stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible' # not resolve to a pseudo-version with a different major version. cp go.mod.orig go.mod ! go get -d github.com/pierrec/lz4@v2.0.8 -stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2' +stderr 'go: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2' # An invalid +incompatible suffix for a canonical version should error out, # not resolve to a pseudo-version. diff --git a/src/cmd/go/testdata/script/mod_list.txt b/src/cmd/go/testdata/script/mod_list.txt index 239c7caa4a..06316cc335 100644 --- a/src/cmd/go/testdata/script/mod_list.txt +++ b/src/cmd/go/testdata/script/mod_list.txt @@ -39,8 +39,8 @@ stdout '^module nonexist: not a known dependency$' stdout '^module rsc.io/quote/buggy: not a known dependency$' ! go list -m nonexist rsc.io/quote/buggy -stderr '^go list -m: module nonexist: not a known dependency' -stderr '^go list -m: module rsc.io/quote/buggy: not a known dependency' +stderr '^go: module nonexist: not a known dependency' +stderr '^go: module rsc.io/quote/buggy: not a known dependency' # Module loader does not interfere with list -e (golang.org/issue/24149). go list -e -f '{{.Error.Err}}' database diff --git a/src/cmd/go/testdata/script/mod_list_sums.txt b/src/cmd/go/testdata/script/mod_list_sums.txt index 86c528f829..6c2f57c2b2 100644 --- a/src/cmd/go/testdata/script/mod_list_sums.txt +++ b/src/cmd/go/testdata/script/mod_list_sums.txt @@ -29,4 +29,4 @@ stderr '^go: updates to go.sum needed, disabled by -mod=readonly$' # # TODO(#41297): This should not be an error either. ! go list -m -mod=readonly -versions rsc.io/sampler -stderr '^go list -m: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' +stderr '^go: rsc\.io/quote@v1\.5\.1: missing go\.sum entry; to add it:\n\tgo mod download rsc\.io/quote$' diff --git a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt index c6bbbb04ec..7eebe266db 100644 --- a/src/cmd/go/testdata/script/mod_list_update_nolatest.txt +++ b/src/cmd/go/testdata/script/mod_list_update_nolatest.txt @@ -26,7 +26,7 @@ stdout '^example.com/nolatest v0.0.0$' # If proxy returns an invalid response, we should see an error. env GOPROXY=$testproxy/invalid ! go list -m -u example.com/nolatest -stderr '^go list -m: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' +stderr '^go: loading module retractions for example.com/nolatest@v0.0.0: invalid response from proxy "[^"]*": invalid character ''i'' looking for beginning of value$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/mod_load_badchain.txt b/src/cmd/go/testdata/script/mod_load_badchain.txt index eb464ab0d3..0c4e5e1714 100644 --- a/src/cmd/go/testdata/script/mod_load_badchain.txt +++ b/src/cmd/go/testdata/script/mod_load_badchain.txt @@ -69,17 +69,17 @@ import ( func Test(t *testing.T) {} -- update-main-expected -- -go get: example.com/badchain/c@v1.1.0: parsing go.mod: +go: example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- update-a-expected -- -go get: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c but was required as: example.com/badchain/c -- list-expected -- -go list -m: example.com/badchain/a@v1.1.0 requires +go: example.com/badchain/a@v1.1.0 requires example.com/badchain/b@v1.1.0 requires example.com/badchain/c@v1.1.0: parsing go.mod: module declares its path as: badchain.example.com/c diff --git a/src/cmd/go/testdata/script/mod_outside.txt b/src/cmd/go/testdata/script/mod_outside.txt index 3b4559405a..6da6314b79 100644 --- a/src/cmd/go/testdata/script/mod_outside.txt +++ b/src/cmd/go/testdata/script/mod_outside.txt @@ -135,12 +135,12 @@ stderr '^go: go.mod file not found in current directory or any parent directory; # 'go get -u all' upgrades the transitive import graph of the main module, # which is empty. ! go get -u all -stderr '^go get: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$' +stderr '^go: cannot match "all": go.mod file not found in current directory or any parent directory; see ''go help modules''$' # 'go get' should check the proposed module graph for consistency, # even though we won't write it anywhere. ! go get -d example.com/printversion@v1.0.0 example.com/version@none -stderr '^go get: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$' +stderr '^go: example.com/printversion@v1.0.0 requires example.com/version@v1.0.0, not example.com/version@none$' # 'go get -d' should download and extract the source code needed to build the requested version. rm -r $GOPATH/pkg/mod/example.com @@ -196,7 +196,7 @@ exists $GOPATH/bin/printversion$GOEXE # 'go install' should fail if a package argument must be resolved to a module. ! go install example.com/printversion -stderr '^go install: version is required when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' +stderr '^go: ''go install'' requires a version when current directory is not in a module\n\tTry ''go install example.com/printversion@latest'' to install the latest version$' # 'go install' should fail if a source file imports a package that must be # resolved to a module. diff --git a/src/cmd/go/testdata/script/mod_prefer_compatible.txt b/src/cmd/go/testdata/script/mod_prefer_compatible.txt index 1b408c3e9e..8e88997a3c 100644 --- a/src/cmd/go/testdata/script/mod_prefer_compatible.txt +++ b/src/cmd/go/testdata/script/mod_prefer_compatible.txt @@ -24,7 +24,7 @@ go list -m github.com/russross/blackfriday@upgrade stdout '^github.com/russross/blackfriday v1\.' ! go list -m github.com/russross/blackfriday@patch -stderr '^go list -m: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' +stderr '^go: github.com/russross/blackfriday@patch: can''t query version "patch" of module github.com/russross/blackfriday: no existing version is required$' # If we're fetching directly from version control, ignored +incompatible # versions should also be omitted by 'go list'. diff --git a/src/cmd/go/testdata/script/mod_proxy_invalid.txt b/src/cmd/go/testdata/script/mod_proxy_invalid.txt index 6427cc1527..63980b839e 100644 --- a/src/cmd/go/testdata/script/mod_proxy_invalid.txt +++ b/src/cmd/go/testdata/script/mod_proxy_invalid.txt @@ -2,7 +2,7 @@ env GO111MODULE=on env GOPROXY=$GOPROXY/invalid ! go list -m rsc.io/quote@latest -stderr '^go list -m: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: module rsc.io/quote: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' ! go list -m rsc.io/quote@1.5.2 -stderr '^go list -m: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' +stderr '^go: rsc.io/quote@1.5.2: invalid version: invalid response from proxy "'$GOPROXY'": invalid character ''i'' looking for beginning of value$' diff --git a/src/cmd/go/testdata/script/mod_query.txt b/src/cmd/go/testdata/script/mod_query.txt index a75f86ed7c..3758732504 100644 --- a/src/cmd/go/testdata/script/mod_query.txt +++ b/src/cmd/go/testdata/script/mod_query.txt @@ -25,7 +25,7 @@ go list -m rsc.io/quote@v1.5.3 -stderr 'go list -m: module rsc.io/quote: no matching versions for query ">v1.5.3"' +stderr 'go: module rsc.io/quote: no matching versions for query ">v1.5.3"' go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3 stdout 'no matching versions for query ">v1.5.3"' diff --git a/src/cmd/go/testdata/script/mod_query_empty.txt b/src/cmd/go/testdata/script/mod_query_empty.txt index f8b6e3e97e..af0871173e 100644 --- a/src/cmd/go/testdata/script/mod_query_empty.txt +++ b/src/cmd/go/testdata/script/mod_query_empty.txt @@ -8,7 +8,7 @@ go mod download example.com/join@v1.1.0 env GOPROXY=file:///$WORK/badproxy cp go.mod.orig go.mod ! go get -d example.com/join/subpkg -stderr 'go get: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' +stderr 'go: example.com/join/subpkg@v0.0.0-20190624000000-123456abcdef: .*' # If @v/list is empty, the 'go' command should still try to resolve # other module paths. @@ -40,7 +40,7 @@ env GOPROXY=file:///$WORK/gatekeeper chmod 0000 $WORK/gatekeeper/example.com/join/subpkg/@latest cp go.mod.orig go.mod ! go get -d example.com/join/subpkg -stderr 'go get: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' +stderr 'go: module example.com/join/subpkg: (invalid response from proxy ".+": invalid character .+|reading file://.*/gatekeeper/example.com/join/subpkg/@latest: .+)' -- go.mod.orig -- module example.com/othermodule diff --git a/src/cmd/go/testdata/script/mod_query_exclude.txt b/src/cmd/go/testdata/script/mod_query_exclude.txt index b001969411..8eae42dec6 100644 --- a/src/cmd/go/testdata/script/mod_query_exclude.txt +++ b/src/cmd/go/testdata/script/mod_query_exclude.txt @@ -19,7 +19,7 @@ stdout '^rsc.io/quote v1.5.1$' # get excluded version cp go.exclude.mod go.exclude.mod.orig ! go get -modfile=go.exclude.mod -d rsc.io/quote@v1.5.0 -stderr '^go get: rsc.io/quote@v1.5.0: excluded by go.mod$' +stderr '^go: rsc.io/quote@v1.5.0: excluded by go.mod$' # get non-excluded version cp go.exclude.mod.orig go.exclude.mod diff --git a/src/cmd/go/testdata/script/mod_query_main.txt b/src/cmd/go/testdata/script/mod_query_main.txt index 39e5841a9c..2a2fa42318 100644 --- a/src/cmd/go/testdata/script/mod_query_main.txt +++ b/src/cmd/go/testdata/script/mod_query_main.txt @@ -6,9 +6,9 @@ go mod download rsc.io/quote@latest # 'go mod download' will not download @upgrade or @patch, since they always # resolve to the main module. go mod download rsc.io/quote@upgrade -stderr '^go mod download: skipping argument rsc.io/quote@upgrade that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@upgrade that resolves to the main module$' go mod download rsc.io/quote@patch -stderr '^go mod download: skipping argument rsc.io/quote@patch that resolves to the main module$' +stderr '^go: skipping download of rsc.io/quote@patch that resolves to the main module$' # 'go list -m' can show a version of the main module. go list -m rsc.io/quote@5d9f230b @@ -31,11 +31,11 @@ stdout '^rsc.io/quote$' # 'go get' will not attempt to upgrade the main module to any specific version. # See also: mod_get_main.txt. ! go get rsc.io/quote@5d9f230b -stderr '^go get: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "5d9f230b" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@v1.5.2 -stderr '^go get: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "v1.5.2" of the main module \(rsc.io/quote\)$' ! go get rsc.io/quote@latest -stderr '^go get: can''t request version "latest" of the main module \(rsc.io/quote\)$' +stderr '^go: can''t request version "latest" of the main module \(rsc.io/quote\)$' -- go.mod -- module rsc.io/quote diff --git a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt index d24f37b788..df752d9716 100644 --- a/src/cmd/go/testdata/script/mod_replace_gopkgin.txt +++ b/src/cmd/go/testdata/script/mod_replace_gopkgin.txt @@ -35,7 +35,7 @@ go list -m gopkg.in/src-d/go-git.v4 # A mismatched gopkg.in path should not be able to replace a different major version. cd ../3-to-gomod-4 ! go list -m gopkg.in/src-d/go-git.v3 -stderr '^go list -m: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' +stderr '^go: gopkg\.in/src-d/go-git\.v3@v3\.2\.0 \(replaced by gopkg\.in/src-d/go-git\.v3@v3\.0\.0-20190801152248-0d1a009cbb60\): version "v3\.0\.0-20190801152248-0d1a009cbb60" invalid: go\.mod has non-\.\.\.\.v3 module path "gopkg\.in/src-d/go-git\.v4" at revision 0d1a009cbb60$' -- 4-to-4/go.mod -- module golang.org/issue/34254 diff --git a/src/cmd/go/testdata/script/mod_retract_fix_version.txt b/src/cmd/go/testdata/script/mod_retract_fix_version.txt index e45758b627..9ae49f53ab 100644 --- a/src/cmd/go/testdata/script/mod_retract_fix_version.txt +++ b/src/cmd/go/testdata/script/mod_retract_fix_version.txt @@ -15,7 +15,7 @@ cmp go.mod go.mod.want # If a retracted version doesn't match the module's major version suffx, # an error should be reported. ! go mod edit -retract=v3.0.1 -stderr '^go mod: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' +stderr '^go: -retract=v3.0.1: version "v3.0.1" invalid: should be v2, not v3$' cp go.mod.mismatch-v2 go.mod ! go list -m all stderr 'go.mod:3: retract rsc.io/quote/v2: version "v3.0.1" invalid: should be v2, not v3$' diff --git a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt index eb00e8405c..d1a5e1236a 100644 --- a/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt +++ b/src/cmd/go/testdata/script/mod_retract_pseudo_base.txt @@ -29,7 +29,7 @@ go list -m vcs-test.golang.org/git/retract-pseudo.git stdout '^vcs-test.golang.org/git/retract-pseudo.git v1.0.1-0.20201009173747-713affd19d7b$' ! go get -d vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371 -stderr '^go get: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' +stderr '^go: vcs-test.golang.org/git/retract-pseudo.git@v1.0.1-0.20201009173747-64c061ed4371: invalid pseudo-version: tag \(v1.0.0\) found on revision 64c061ed4371 is already canonical, so should not be replaced with a pseudo-version derived from that tag$' -- retract-pseudo.sh -- #!/bin/bash diff --git a/src/cmd/go/testdata/script/mod_run_nonmain.txt b/src/cmd/go/testdata/script/mod_run_nonmain.txt index 036755d2d1..8435fc05b4 100644 --- a/src/cmd/go/testdata/script/mod_run_nonmain.txt +++ b/src/cmd/go/testdata/script/mod_run_nonmain.txt @@ -7,7 +7,7 @@ stderr '^package example.net/nonmain is not a main package$' ! go run ./... stderr '^go: warning: "\./\.\.\." matched only non-main packages$' -stderr '^go run: no packages loaded from \./\.\.\.$' +stderr '^go: no packages loaded from \./\.\.\.$' -- go.mod -- module example.net/nonmain diff --git a/src/cmd/go/testdata/script/mod_run_pkg_version.txt b/src/cmd/go/testdata/script/mod_run_pkg_version.txt index e921fab508..c3a218d553 100644 --- a/src/cmd/go/testdata/script/mod_run_pkg_version.txt +++ b/src/cmd/go/testdata/script/mod_run_pkg_version.txt @@ -21,7 +21,7 @@ env GO111MODULE=on cd m cp go.mod go.mod.orig ! go list -m all -stderr '^go list -m: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' +stderr '^go: example.com/cmd@v1.1.0-doesnotexist: missing go.sum entry; to add it:\n\tgo mod download example.com/cmd$' go run example.com/cmd/a@v1.0.0 stdout '^a@v1.0.0$' cmp go.mod go.mod.orig @@ -92,12 +92,12 @@ package main func main() {} -- replace-err -- -go run: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): +go: example.com/cmd/a@v1.0.0-replace (in example.com/cmd@v1.0.0-replace): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. -- exclude-err -- -go run: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): +go: example.com/cmd/a@v1.0.0-exclude (in example.com/cmd@v1.0.0-exclude): The go.mod file for the module providing named packages contains one or more exclude directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module. diff --git a/src/cmd/go/testdata/script/mod_sum_readonly.txt b/src/cmd/go/testdata/script/mod_sum_readonly.txt index 113f13ea39..57c5bbeefd 100644 --- a/src/cmd/go/testdata/script/mod_sum_readonly.txt +++ b/src/cmd/go/testdata/script/mod_sum_readonly.txt @@ -4,7 +4,7 @@ env GO111MODULE=on # When a sum is needed to load the build list, we get an error for the # specific module. The .mod file is not downloaded, and go.sum is not written. ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod ! exists go.sum @@ -12,7 +12,7 @@ stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo # we should see the same error. cp go.sum.h2only go.sum ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2: missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod cmp go.sum go.sum.h2only rm go.sum @@ -21,7 +21,7 @@ rm go.sum cp go.mod go.mod.orig go mod edit -replace rsc.io/quote@v1.5.2=rsc.io/quote@v1.5.1 ! go list -m all -stderr '^go list -m: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' +stderr '^go: rsc.io/quote@v1.5.2 \(replaced by rsc.io/quote@v1.5.1\): missing go.sum entry; to add it:\n\tgo mod download rsc.io/quote$' ! exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.mod ! exists go.sum cp go.mod.orig go.mod diff --git a/src/cmd/go/testdata/script/mod_sumdb.txt b/src/cmd/go/testdata/script/mod_sumdb.txt index fa3483c5cb..dd791be1d4 100644 --- a/src/cmd/go/testdata/script/mod_sumdb.txt +++ b/src/cmd/go/testdata/script/mod_sumdb.txt @@ -9,7 +9,7 @@ env dbname=localhost.localdev/sumdb cp go.mod.orig go.mod env GOSUMDB=$sumdb' '$proxy/sumdb-wrong ! go get -d rsc.io/quote -stderr 'go get: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' +stderr 'go: rsc.io/quote@v1.5.2: verifying module: checksum mismatch' stderr 'downloaded: h1:3fEy' stderr 'localhost.localdev/sumdb: h1:wrong' stderr 'SECURITY ERROR\nThis download does NOT match the one reported by the checksum server.' diff --git a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt index 22fcbf3de8..575c7c4817 100644 --- a/src/cmd/go/testdata/script/mod_sumdb_file_path.txt +++ b/src/cmd/go/testdata/script/mod_sumdb_file_path.txt @@ -13,7 +13,7 @@ env GOPATH=$WORK/gopath1 [windows] env GOPROXY=file:///$WORK/sumproxy,https://proxy.golang.org [!windows] env GOPROXY=file://$WORK/sumproxy,https://proxy.golang.org ! go get -d golang.org/x/text@v0.3.2 -stderr '^go get: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' +stderr '^go: golang.org/x/text@v0.3.2: verifying module: golang.org/x/text@v0.3.2: reading file://.*/sumdb/sum.golang.org/lookup/golang.org/x/text@v0.3.2: (no such file or directory|.*cannot find the path specified.*)' # If the proxy does not claim to support the database, # checksum verification should fall through to the next proxy, diff --git a/src/cmd/go/testdata/script/mod_tidy_compat.txt b/src/cmd/go/testdata/script/mod_tidy_compat.txt index 29cae17881..18b297da60 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat.txt @@ -50,7 +50,7 @@ cmp stdout m_all.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' +stderr '^go: example.net/lazy@v0.1.0 requires\n\texample.com/version@v1.0.1: missing go.sum entry; to add it:\n\tgo mod download example.com/version$' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt index c544cb7413..44bc58cc6c 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt @@ -62,7 +62,7 @@ cmp stdout all-m.txt go mod edit -go=1.16 ! go list -m all -stderr '^go list -m: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' +stderr '^go: example\.net/indirect@v0\.1\.0 requires\n\texample\.net/ambiguous@v0\.1\.0: missing go\.sum entry; to add it:\n\tgo mod download example\.net/ambiguous\n' -- go.mod -- diff --git a/src/cmd/go/testdata/script/mod_tidy_too_new.txt b/src/cmd/go/testdata/script/mod_tidy_too_new.txt index b9c53b510d..8c34a997c9 100644 --- a/src/cmd/go/testdata/script/mod_tidy_too_new.txt +++ b/src/cmd/go/testdata/script/mod_tidy_too_new.txt @@ -9,7 +9,7 @@ cp go.mod go.mod.orig # would look like. ! go mod tidy -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.orig @@ -18,7 +18,7 @@ cmp go.mod go.mod.orig cp go.mod.orig go.mod go mod tidy -e -stderr 'go mod tidy: go.mod file indicates go 2000.0, but maximum supported version is '$goversion'$' +stderr 'go: go.mod file indicates go 2000.0, but maximum version supported by tidy is '$goversion'$' cmp go.mod go.mod.tidy diff --git a/src/cmd/go/testdata/script/mod_upgrade_patch.txt b/src/cmd/go/testdata/script/mod_upgrade_patch.txt index 8b34f8bf27..b8c178c75e 100644 --- a/src/cmd/go/testdata/script/mod_upgrade_patch.txt +++ b/src/cmd/go/testdata/script/mod_upgrade_patch.txt @@ -11,7 +11,7 @@ stdout '^patch.example.com/indirect v1.0.0' # @patch should be rejected for modules not already in the build list. ! go get -d patch.example.com/depofdirectpatch@patch -stderr '^go get: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' +stderr '^go: can''t query version "patch" of module patch.example.com/depofdirectpatch: no existing version is required$' cmp go.mod.orig go.mod # get -u=patch, with no arguments, should patch-update all dependencies @@ -38,7 +38,7 @@ cp go.mod.orig go.mod go mod edit -droprequire=patch.example.com/direct cp go.mod go.mod.dropped ! go get -d all@patch -stderr '^go get all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' +stderr '^go: all@patch: can''t query version "patch" of module patch.example.com/direct: no existing version is required$' cmp go.mod.dropped go.mod # Requesting the direct dependency with -u=patch but without an explicit version @@ -86,7 +86,7 @@ stdout '^patch.example.com/indirect v1.0.1' # Standard library packages cannot be upgraded explicitly. cp go.mod.orig go.mod ! go get cmd/vet@patch -stderr 'go get: can''t request explicit version "patch" of standard library package cmd/vet$' +stderr 'go: can''t request explicit version "patch" of standard library package cmd/vet$' # However, standard-library packages without explicit versions are fine. go get -d -u=patch -d cmd/go diff --git a/src/cmd/go/testdata/script/mod_vendor.txt b/src/cmd/go/testdata/script/mod_vendor.txt index 2622916f61..4eb80c2332 100644 --- a/src/cmd/go/testdata/script/mod_vendor.txt +++ b/src/cmd/go/testdata/script/mod_vendor.txt @@ -40,15 +40,15 @@ stdout '^v1.0.0 $' # -mod=vendor should cause 'go list' flags that look up versions to fail. ! go list -mod=vendor -versions -m x -stderr '^go list -m: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available versions using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' ! go list -mod=vendor -u -m x -stderr '^go list -m: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' +stderr '^go: can''t determine available upgrades using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)$' # 'go list -mod=vendor -m' on a transitive dependency that does not # provide vendored packages should give a helpful error rather than # 'not a known dependency'. ! go list -mod=vendor -f '{{.Version}} {{.Dir}}' -m diamondright -stderr 'go list -m: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: module diamondright: can''t resolve module using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go list -mod=mod' should report packages outside the import graph, # but 'go list -mod=vendor' should error out for them. diff --git a/src/cmd/go/testdata/script/mod_vendor_auto.txt b/src/cmd/go/testdata/script/mod_vendor_auto.txt index b0ea907206..96db5c1600 100644 --- a/src/cmd/go/testdata/script/mod_vendor_auto.txt +++ b/src/cmd/go/testdata/script/mod_vendor_auto.txt @@ -17,10 +17,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]printversion$' stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # An explicit -mod=mod should force the vendor directory to be ignored. env GOFLAGS=-mod=mod @@ -105,10 +105,10 @@ stdout '^'$WORK'[/\\]auto[/\\]vendor[/\\]example.com[/\\]version$' # ...but 'go list -m' should continue to fail, this time without # referring to a -mod default that the user didn't set. ! go list -m all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' ! go list -m -f '{{.Dir}}' all -stderr 'go list -m: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' +stderr 'go: can''t compute ''all'' using the vendor directory\n\t\(Use -mod=mod or -mod=readonly to bypass.\)' # 'go mod init' should work if there is already a GOPATH-mode vendor directory diff --git a/src/cmd/go/testdata/script/mod_vendor_embed.txt b/src/cmd/go/testdata/script/mod_vendor_embed.txt index be114159a1..b14fd99156 100644 --- a/src/cmd/go/testdata/script/mod_vendor_embed.txt +++ b/src/cmd/go/testdata/script/mod_vendor_embed.txt @@ -6,11 +6,11 @@ cmp vendor/example.com/a/subdir/test/xtest/embed.txt a/subdir/test/xtest/embed.t cd broken_no_matching_files ! go mod vendor -stderr 'go mod vendor: pattern foo.txt: no matching files found' +stderr 'go: pattern foo.txt: no matching files found' cd ../broken_bad_pattern ! go mod vendor -stderr 'go mod vendor: pattern ../foo.txt: invalid pattern syntax' +stderr 'go: pattern ../foo.txt: invalid pattern syntax' # matchPotentialSourceFile prunes out tests and unbuilt code. # Make sure that they are vendored if they are embedded files. diff --git a/src/cmd/go/testdata/script/run_wildcard.txt b/src/cmd/go/testdata/script/run_wildcard.txt index 72036d1d8d..3e7e7b7e42 100644 --- a/src/cmd/go/testdata/script/run_wildcard.txt +++ b/src/cmd/go/testdata/script/run_wildcard.txt @@ -4,4 +4,4 @@ env GO111MODULE=off # go run x/... should not panic when directory x doesn't exist. ! go run nonexistent/... -stderr '^go run: no packages loaded from nonexistent/...$' +stderr '^go: no packages loaded from nonexistent/...$' diff --git a/src/cmd/go/testdata/script/test_flag.txt b/src/cmd/go/testdata/script/test_flag.txt index 0142b3f308..d168cfe6a8 100644 --- a/src/cmd/go/testdata/script/test_flag.txt +++ b/src/cmd/go/testdata/script/test_flag.txt @@ -9,13 +9,13 @@ go test -count=1 -custom -args -v=7 # However, it should be an error to use custom flags when -i or -c are used, # since we know for sure that no test binary will run at all. ! go test -i -custom -stderr '^go test: unknown flag -custom cannot be used with -i$' +stderr '^go: unknown flag -custom cannot be used with -i$' ! go test -c -custom -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' # The same should apply even if -c or -i come after a custom flag. ! go test -custom -c -stderr '^go test: unknown flag -custom cannot be used with -c$' +stderr '^go: unknown flag -custom cannot be used with -c$' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/test_race_install.txt b/src/cmd/go/testdata/script/test_race_install.txt index 8b1f343a32..a1d47a7dd3 100644 --- a/src/cmd/go/testdata/script/test_race_install.txt +++ b/src/cmd/go/testdata/script/test_race_install.txt @@ -15,4 +15,4 @@ go 1.16 -- pkg/pkg.go -- package p -- stderr.txt -- -go test: -i flag is deprecated +go: -i flag is deprecated -- cgit v1.2.3-54-g00ecf From 2da3375e9b4980e368a8641f54cc53c4af4d1a12 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 20 Aug 2021 16:55:04 -0700 Subject: runtime: in adjustTimers back up as far as necessary When the adjustTimers function removed a timer it assumed it was sufficient to continue the heap traversal at that position. However, in some cases a timer will be moved to an earlier position in the heap. If that timer is timerModifiedEarlier, that can leave timerModifiedEarliest not correctly representing the earlier such timer. Fix the problem by restarting the heap traversal at the earliest changed position. Fixes #47762 Change-Id: I152bbe62793ee40a680baf49967bcb89b1f94764 Reviewed-on: https://go-review.googlesource.com/c/go/+/343882 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Michael Knyszek --- src/runtime/time.go | 30 ++++++++++++++-------- src/time/sleep_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/src/runtime/time.go b/src/runtime/time.go index ad267c3365..46e9a8c2ab 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -367,9 +367,9 @@ func deltimer(t *timer) bool { // dodeltimer removes timer i from the current P's heap. // We are locked on the P when this is called. -// It reports whether it saw no problems due to races. +// It returns the smallest changed index in pp.timers. // The caller must have locked the timers for pp. -func dodeltimer(pp *p, i int) { +func dodeltimer(pp *p, i int) int { if t := pp.timers[i]; t.pp.ptr() != pp { throw("dodeltimer: wrong P") } else { @@ -381,16 +381,18 @@ func dodeltimer(pp *p, i int) { } pp.timers[last] = nil pp.timers = pp.timers[:last] + smallestChanged := i if i != last { // Moving to i may have moved the last timer to a new parent, // so sift up to preserve the heap guarantee. - siftupTimer(pp.timers, i) + smallestChanged = siftupTimer(pp.timers, i) siftdownTimer(pp.timers, i) } if i == 0 { updateTimer0When(pp) } atomic.Xadd(&pp.numTimers, -1) + return smallestChanged } // dodeltimer0 removes timer 0 from the current P's heap. @@ -675,13 +677,14 @@ func adjusttimers(pp *p, now int64) { switch s := atomic.Load(&t.status); s { case timerDeleted: if atomic.Cas(&t.status, s, timerRemoving) { - dodeltimer(pp, i) + changed := dodeltimer(pp, i) if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { badTimer() } atomic.Xadd(&pp.deletedTimers, -1) - // Look at this heap position again. - i-- + // Go back to the earliest changed heap entry. + // "- 1" because the loop will add 1. + i = changed - 1 } case timerModifiedEarlier, timerModifiedLater: if atomic.Cas(&t.status, s, timerMoving) { @@ -691,10 +694,11 @@ func adjusttimers(pp *p, now int64) { // We don't add it back yet because the // heap manipulation could cause our // loop to skip some other timer. - dodeltimer(pp, i) + changed := dodeltimer(pp, i) moved = append(moved, t) - // Look at this heap position again. - i-- + // Go back to the earliest changed heap entry. + // "- 1" because the loop will add 1. + i = changed - 1 } case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving: badTimer() @@ -1044,7 +1048,10 @@ func timeSleepUntil() (int64, *p) { // "panic holding locks" message. Instead, we panic while not // holding a lock. -func siftupTimer(t []*timer, i int) { +// siftupTimer puts the timer at position i in the right place +// in the heap by moving it up toward the top of the heap. +// It returns the smallest changed index. +func siftupTimer(t []*timer, i int) int { if i >= len(t) { badTimer() } @@ -1064,8 +1071,11 @@ func siftupTimer(t []*timer, i int) { if tmp != t[i] { t[i] = tmp } + return i } +// siftdownTimer puts the timer at position i in the right place +// in the heap by moving it down toward the bottom of the heap. func siftdownTimer(t []*timer, i int) { n := len(t) if i >= n { diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go index e0172bf5e0..c48e704eb7 100644 --- a/src/time/sleep_test.go +++ b/src/time/sleep_test.go @@ -7,6 +7,7 @@ package time_test import ( "errors" "fmt" + "math/rand" "runtime" "strings" "sync" @@ -561,6 +562,72 @@ func TestTimerModifiedEarlier(t *testing.T) { } } +// Test that rapidly moving timers earlier and later doesn't cause +// some of the sleep times to be lost. +// Issue 47762 +func TestAdjustTimers(t *testing.T) { + var rnd = rand.New(rand.NewSource(Now().UnixNano())) + + timers := make([]*Timer, 100) + states := make([]int, len(timers)) + indices := rnd.Perm(len(timers)) + + for len(indices) != 0 { + var ii = rnd.Intn(len(indices)) + var i = indices[ii] + + var timer = timers[i] + var state = states[i] + states[i]++ + + switch state { + case 0: + timers[i] = NewTimer(0) + case 1: + <-timer.C // Timer is now idle. + + // Reset to various long durations, which we'll cancel. + case 2: + if timer.Reset(1 * Minute) { + panic("shouldn't be active (1)") + } + case 4: + if timer.Reset(3 * Minute) { + panic("shouldn't be active (3)") + } + case 6: + if timer.Reset(2 * Minute) { + panic("shouldn't be active (2)") + } + + // Stop and drain a long-duration timer. + case 3, 5, 7: + if !timer.Stop() { + t.Logf("timer %d state %d Stop returned false", i, state) + <-timer.C + } + + // Start a short-duration timer we expect to select without blocking. + case 8: + if timer.Reset(0) { + t.Fatal("timer.Reset returned true") + } + case 9: + now := Now() + <-timer.C + dur := Since(now) + if dur > 750*Millisecond { + t.Errorf("timer %d took %v to complete", i, dur) + } + + // Timer is done. Swap with tail and remove. + case 10: + indices[ii] = indices[len(indices)-1] + indices = indices[:len(indices)-1] + } + } +} + // Benchmark timer latency when the thread that creates the timer is busy with // other work and the timers must be serviced by other threads. // https://golang.org/issue/38860 -- cgit v1.2.3-54-g00ecf From 9fc28892cb88dd4c7b0552137b97c1692c23e46b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Sep 2021 13:09:19 -0700 Subject: cmd/compile/internal/types2: export TypeHash, return value without blanks Change the typeWriter to produce blank-free hashes where easily possible if used as a type hasher, and replace remaining blanks with '#' is needed. Exported Environment.TypeHash for use by the compiler. Change-Id: Icbd364c207f9c139a7a1844bb695512a0c56a4e4 Reviewed-on: https://go-review.googlesource.com/c/go/+/349990 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/environment.go | 9 ++--- src/cmd/compile/internal/types2/instantiate.go | 2 +- src/cmd/compile/internal/types2/named.go | 2 +- src/cmd/compile/internal/types2/subst.go | 2 +- src/cmd/compile/internal/types2/typestring.go | 46 ++++++++++++++++++-------- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go index 816139bbb4..5ef8855a1b 100644 --- a/src/cmd/compile/internal/types2/environment.go +++ b/src/cmd/compile/internal/types2/environment.go @@ -5,6 +5,7 @@ package types2 import ( "bytes" + "strings" "sync" ) @@ -28,11 +29,11 @@ func NewEnvironment() *Environment { } } -// typeHash returns a string representation of typ, which can be used as an exact +// TypeHash returns a string representation of typ, which can be used as an exact // type hash: types that are identical produce identical string representations. // If typ is a *Named type and targs is not empty, typ is printed as if it were -// instantiated with targs. -func (env *Environment) typeHash(typ Type, targs []Type) string { +// instantiated with targs. The result is guaranteed to not contain blanks (" "). +func (env *Environment) TypeHash(typ Type, targs []Type) string { assert(env != nil) assert(typ != nil) var buf bytes.Buffer @@ -56,7 +57,7 @@ func (env *Environment) typeHash(typ Type, targs []Type) string { } } - return buf.String() + return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 } // typeForHash returns the recorded type for the type hash h, if it exists. diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 469ceea5c4..fdb87e75f6 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -108,7 +108,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Envi case *Named: var h string if env != nil { - h = env.typeHash(t, targs) + h = env.TypeHash(t, targs) // typ may already have been instantiated with identical type arguments. In // that case, re-use the existing instance. if named := env.typeForHash(h, nil); named != nil { diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 99410aedfb..46487d1cae 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -262,7 +262,7 @@ func (n *Named) expand(env *Environment) *Named { // instance in the process of expansion. env = NewEnvironment() } - h := env.typeHash(n.orig, n.targs.list()) + h := env.TypeHash(n.orig, n.targs.list()) // add the instance to the environment to avoid infinite recursion. // addInstance may return a different, existing instance, but we // shouldn't return that instance from expand. diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 4627dd3c5b..8d96494af0 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -211,7 +211,7 @@ func (subst *subster) typ(typ Type) Type { } // before creating a new named type, check if we have this one already - h := subst.env.typeHash(t.orig, newTArgs) + h := subst.env.TypeHash(t.orig, newTArgs) dump(">>> new type hash: %s", h) if named := subst.env.typeForHash(h, nil); named != nil { dump(">>> found %s", named) diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 39ba278d53..71da37c3a1 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -8,7 +8,6 @@ package types2 import ( "bytes" - "fmt" "strconv" "unicode/utf8" ) @@ -83,14 +82,29 @@ func newTypeHasher(buf *bytes.Buffer, env *Environment) *typeWriter { return &typeWriter{buf, make(map[Type]bool), nil, env} } -func (w *typeWriter) byte(b byte) { w.buf.WriteByte(b) } -func (w *typeWriter) string(s string) { w.buf.WriteString(s) } -func (w *typeWriter) writef(format string, args ...interface{}) { fmt.Fprintf(w.buf, format, args...) } +func (w *typeWriter) byte(b byte) { + if w.env != nil { + if b == ' ' { + b = '#' + } + w.buf.WriteByte(b) + return + } + w.buf.WriteByte(b) + if b == ',' || b == ';' { + w.buf.WriteByte(' ') + } +} + +func (w *typeWriter) string(s string) { + w.buf.WriteString(s) +} + func (w *typeWriter) error(msg string) { if w.env != nil { panic(msg) } - w.string("<" + msg + ">") + w.buf.WriteString("<" + msg + ">") } func (w *typeWriter) typ(typ Type) { @@ -117,7 +131,9 @@ func (w *typeWriter) typ(typ Type) { w.string(t.name) case *Array: - w.writef("[%d]", t.len) + w.byte('[') + w.string(strconv.FormatInt(t.len, 10)) + w.byte(']') w.typ(t.elem) case *Slice: @@ -128,7 +144,7 @@ func (w *typeWriter) typ(typ Type) { w.string("struct{") for i, f := range t.fields { if i > 0 { - w.string("; ") + w.byte(';') } // This doesn't do the right thing for embedded type // aliases where we should print the alias name, not @@ -139,7 +155,11 @@ func (w *typeWriter) typ(typ Type) { } w.typ(f.typ) if tag := t.Tag(i); tag != "" { - w.writef(" %q", tag) + w.byte(' ') + // TODO(gri) If tag contains blanks, replacing them with '#' + // in Environment.TypeHash may produce another tag + // accidentally. + w.string(strconv.Quote(tag)) } } w.byte('}') @@ -177,7 +197,7 @@ func (w *typeWriter) typ(typ Type) { first := true for _, m := range t.methods { if !first { - w.string("; ") + w.byte(';') } first = false w.string(m.name) @@ -185,7 +205,7 @@ func (w *typeWriter) typ(typ Type) { } for _, typ := range t.embeddeds { if !first { - w.string("; ") + w.byte(';') } first = false w.typ(typ) @@ -279,7 +299,7 @@ func (w *typeWriter) typeList(list []Type) { w.byte('[') for i, typ := range list { if i > 0 { - w.string(", ") + w.byte(',') } w.typ(typ) } @@ -303,7 +323,7 @@ func (w *typeWriter) tParamList(list []*TypeParam) { w.byte(' ') w.typ(prev) } - w.string(", ") + w.byte(',') } prev = tpar.bound w.typ(tpar) @@ -327,7 +347,7 @@ func (w *typeWriter) tuple(tup *Tuple, variadic bool) { if tup != nil { for i, v := range tup.vars { if i > 0 { - w.string(", ") + w.byte(',') } // parameter names are ignored for type identity and thus type hashes if w.env == nil && v.name != "" { -- cgit v1.2.3-54-g00ecf From b26d325cb1fda20129deaef6e6a666efdc8f0140 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Sep 2021 13:18:40 -0700 Subject: cmd/compile/internal/types2: remove some unnecessary loading/expansion of Named types This is a clean port of CL 349409 from go/types to types2. Change-Id: I2deb9ce46e6dcda736fda2169912c02163930d7d Reviewed-on: https://go-review.googlesource.com/c/go/+/349991 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/predicates.go | 3 --- src/cmd/compile/internal/types2/unify.go | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index 473d22675f..aa797fccc7 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -302,9 +302,6 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { // Two named types are identical if their type names originate // in the same type declaration. if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - xargs := x.TypeArgs().list() yargs := y.TypeArgs().list() diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index a1e5b3679b..bb69f0d27b 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -428,9 +428,6 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool { case *Named: if y, ok := y.(*Named); ok { - x.expand(nil) - y.expand(nil) - xargs := x.targs.list() yargs := y.targs.list() -- cgit v1.2.3-54-g00ecf From 738cebb1747335c182af64614041ceb2b2303f74 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Sep 2021 13:23:37 -0700 Subject: cmd/compile/internal/types2: implement Identical for *Union types This is a clean port of CL 349413 from go/types to types2. Change-Id: I18bad5e29b1e719b30a73fb2aa32fe252538496e Reviewed-on: https://go-review.googlesource.com/c/go/+/349992 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/api_test.go | 42 +++++++++++++++++++++++++++ src/cmd/compile/internal/types2/predicates.go | 7 +++++ 2 files changed, 49 insertions(+) diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 3ec0d78a23..5a20738631 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -1645,6 +1645,48 @@ func TestIdentical_issue15173(t *testing.T) { } } +func TestIdenticalUnions(t *testing.T) { + tname := NewTypeName(nopos, nil, "myInt", nil) + myInt := NewNamed(tname, Typ[Int], nil) + tmap := map[string]*Term{ + "int": NewTerm(false, Typ[Int]), + "~int": NewTerm(true, Typ[Int]), + "string": NewTerm(false, Typ[String]), + "~string": NewTerm(true, Typ[String]), + "myInt": NewTerm(false, myInt), + } + makeUnion := func(s string) *Union { + parts := strings.Split(s, "|") + var terms []*Term + for _, p := range parts { + term := tmap[p] + if term == nil { + t.Fatalf("missing term %q", p) + } + terms = append(terms, term) + } + return NewUnion(terms) + } + for _, test := range []struct { + x, y string + want bool + }{ + // These tests are just sanity checks. The tests for type sets and + // interfaces provide much more test coverage. + {"int|~int", "~int", true}, + {"myInt|~int", "~int", true}, + {"int|string", "string|int", true}, + {"int|int|string", "string|int", true}, + {"myInt|string", "int|string", false}, + } { + x := makeUnion(test.x) + y := makeUnion(test.y) + if got := Identical(x, y); got != test.want { + t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got) + } + } +} + func TestIssue15305(t *testing.T) { const src = "package p; func f() int16; var _ = f(undef)" f, err := parseSrc("issue15305.go", src) diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go index aa797fccc7..74ad3da72c 100644 --- a/src/cmd/compile/internal/types2/predicates.go +++ b/src/cmd/compile/internal/types2/predicates.go @@ -225,6 +225,13 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { identical(x.results, y.results, cmpTags, p) } + case *Union: + if y, _ := y.(*Union); y != nil { + xset := computeUnionTypeSet(nil, nopos, x) + yset := computeUnionTypeSet(nil, nopos, y) + return xset.terms.equal(yset.terms) + } + case *Interface: // Two interface types are identical if they describe the same type sets. // With the existing implementation restriction, this simplifies to: -- cgit v1.2.3-54-g00ecf From 3100f54f209b9dc3c277e028d70850c975a2de54 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Sep 2021 17:02:30 -0700 Subject: cmd/compile/internal/types2: merge Named type loading and expansion Clean port of CL 349410 from go/types to types2 with 2 adjustments: using syntax.Pos instead of token.Pos, and using TypeHash instead of typeHash. Fixes #47887. Change-Id: Ifd8495e4187b5e30aaf80702768d82aad5e10cf4 Reviewed-on: https://go-review.googlesource.com/c/go/+/349995 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/decl.go | 4 +- src/cmd/compile/internal/types2/instantiate.go | 3 +- src/cmd/compile/internal/types2/lookup.go | 2 +- src/cmd/compile/internal/types2/named.go | 114 ++++++++------------- src/cmd/compile/internal/types2/object.go | 16 ++- src/cmd/compile/internal/types2/signature.go | 2 +- src/cmd/compile/internal/types2/subst.go | 39 +++---- .../types2/testdata/fixedbugs/issue47887.go2 | 28 +++++ src/cmd/compile/internal/types2/type.go | 2 +- 9 files changed, 114 insertions(+), 96 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index 905c21426c..26e050511e 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo { } case *Named: - t.expand(check.conf.Environment) + t.resolve(check.conf.Environment) // don't touch the type if it is from a different package or the Universe scope // (doing so would lead to a race condition - was issue #35049) @@ -715,7 +715,7 @@ func (check *Checker) collectMethods(obj *TypeName) { } if base != nil { - base.load() // TODO(mdempsky): Probably unnecessary. + base.resolve(nil) // TODO(mdempsky): Probably unnecessary. base.methods = append(base.methods, m) } } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index fdb87e75f6..5a6a13a107 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -116,9 +116,10 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Envi } } tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) - named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is loaded + named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved named.targs = NewTypeList(targs) named.instPos = &pos + named.resolver = expandNamed if env != nil { // It's possible that we've lost a race to add named to the environment. // In this case, use whichever instance is recorded in the environment. diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 81bac7b6ff..0e7a2b70e2 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -122,7 +122,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o seen[named] = true // look for a matching attached method - named.load() + named.resolve(nil) if i, m := lookupMethod(named.methods, pkg, name); m != nil { // potential match // caution: method may not have a proper signature yet diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 46487d1cae..7883b7347b 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -22,8 +22,9 @@ type Named struct { targs *TypeList // type arguments (after instantiation), or nil methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily - resolve func(*Named) ([]*TypeParam, Type, []*Func) - once sync.Once + // resolver may be provided to lazily resolve type parameters, underlying, and methods. + resolver func(*Environment, *Named) (tparams *TypeParamList, underlying Type, methods []*Func) + once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing } // NewNamed returns a new named type for the given type name, underlying type, and associated methods. @@ -36,43 +37,22 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named { return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods) } -func (t *Named) load() *Named { - // If t is an instantiated type, it derives its methods and tparams from its - // base type. Since we expect type parameters and methods to be set after a - // call to load, we must load the base and copy here. - // - // underlying is set when t is expanded. - // - // By convention, a type instance is loaded iff its tparams are set. - if t.targs.Len() > 0 && t.tparams == nil { - t.orig.load() - t.tparams = t.orig.tparams - t.methods = t.orig.methods - } - if t.resolve == nil { +func (t *Named) resolve(env *Environment) *Named { + if t.resolver == nil { return t } t.once.Do(func() { - // TODO(mdempsky): Since we're passing t to resolve anyway + // TODO(mdempsky): Since we're passing t to the resolver anyway // (necessary because types2 expects the receiver type for methods // on defined interface types to be the Named rather than the // underlying Interface), maybe it should just handle calling // SetTypeParams, SetUnderlying, and AddMethod instead? Those - // methods would need to support reentrant calls though. It would + // methods would need to support reentrant calls though. It would // also make the API more future-proof towards further extensions // (like SetTypeParams). - - tparams, underlying, methods := t.resolve(t) - - switch underlying.(type) { - case nil, *Named: - panic("invalid underlying type") - } - - t.tparams = bindTParams(tparams) - t.underlying = underlying - t.methods = methods + t.tparams, t.underlying, t.methods = t.resolver(env, t) + t.fromRHS = t.underlying // for cycle detection }) return t } @@ -121,19 +101,19 @@ func (t *Named) Orig() *Named { return t.orig } // TypeParams returns the type parameters of the named type t, or nil. // The result is non-nil for an (originally) parameterized type even if it is instantiated. -func (t *Named) TypeParams() *TypeParamList { return t.load().tparams } +func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams } // SetTypeParams sets the type parameters of the named type t. -func (t *Named) SetTypeParams(tparams []*TypeParam) { t.load().tparams = bindTParams(tparams) } +func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) } // TypeArgs returns the type arguments used to instantiate the named type t. func (t *Named) TypeArgs() *TypeList { return t.targs } // NumMethods returns the number of explicit methods whose receiver is named type t. -func (t *Named) NumMethods() int { return len(t.load().methods) } +func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) } // Method returns the i'th method of named type t for 0 <= i < t.NumMethods(). -func (t *Named) Method(i int) *Func { return t.load().methods[i] } +func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] } // SetUnderlying sets the underlying type and marks t as complete. func (t *Named) SetUnderlying(underlying Type) { @@ -143,18 +123,18 @@ func (t *Named) SetUnderlying(underlying Type) { if _, ok := underlying.(*Named); ok { panic("underlying type must not be *Named") } - t.load().underlying = underlying + t.resolve(nil).underlying = underlying } // AddMethod adds method m unless it is already in the method list. func (t *Named) AddMethod(m *Func) { - t.load() + t.resolve(nil) if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 { t.methods = append(t.methods, m) } } -func (t *Named) Underlying() Type { return t.load().expand(nil).underlying } +func (t *Named) Underlying() Type { return t.resolve(nil).underlying } func (t *Named) String() string { return TypeString(t, nil) } // ---------------------------------------------------------------------------- @@ -240,43 +220,37 @@ func (n *Named) setUnderlying(typ Type) { } } -// expand ensures that the underlying type of n is instantiated. +// expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func (n *Named) expand(env *Environment) *Named { - if n.instPos != nil { - // n must be loaded before instantiation, in order to have accurate - // tparams. This is done implicitly by the call to n.TypeParams, but making - // it explicit is harmless: load is idempotent. - n.load() - var u Type - if n.check.validateTArgLen(*n.instPos, n.tparams.Len(), n.targs.Len()) { - // TODO(rfindley): handling an optional Checker and Environment here (and - // in subst) feels overly complicated. Can we simplify? - if env == nil { - if n.check != nil { - env = n.check.conf.Environment - } else { - // If we're instantiating lazily, we might be outside the scope of a - // type-checking pass. In that case we won't have a pre-existing - // environment, but don't want to create a duplicate of the current - // instance in the process of expansion. - env = NewEnvironment() - } - h := env.TypeHash(n.orig, n.targs.list()) - // add the instance to the environment to avoid infinite recursion. - // addInstance may return a different, existing instance, but we - // shouldn't return that instance from expand. - env.typeForHash(h, n) +func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { + n.orig.resolve(env) + + var u Type + if n.check.validateTArgLen(*n.instPos, n.orig.tparams.Len(), n.targs.Len()) { + // TODO(rfindley): handling an optional Checker and Environment here (and + // in subst) feels overly complicated. Can we simplify? + if env == nil { + if n.check != nil { + env = n.check.conf.Environment + } else { + // If we're instantiating lazily, we might be outside the scope of a + // type-checking pass. In that case we won't have a pre-existing + // environment, but don't want to create a duplicate of the current + // instance in the process of expansion. + env = NewEnvironment() } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.TypeParams().list(), n.targs.list()), env) - } else { - u = Typ[Invalid] + h := env.TypeHash(n.orig, n.targs.list()) + // add the instance to the environment to avoid infinite recursion. + // addInstance may return a different, existing instance, but we + // shouldn't return that instance from expand. + env.typeForHash(h, n) } - n.underlying = u - n.fromRHS = u - n.instPos = nil + u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + } else { + u = Typ[Invalid] } - return n + n.instPos = nil + return n.orig.tparams, u, n.orig.methods } // safeUnderlying returns the underlying of typ without expanding instances, to @@ -285,7 +259,7 @@ func (n *Named) expand(env *Environment) *Named { // TODO(rfindley): eliminate this function or give it a better name. func safeUnderlying(typ Type) Type { if t, _ := typ.(*Named); t != nil { - return t.load().underlying + return t.resolve(nil).underlying } return typ.Underlying() } diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go index 9bc2e285ce..540cb3f44f 100644 --- a/src/cmd/compile/internal/types2/object.go +++ b/src/cmd/compile/internal/types2/object.go @@ -278,9 +278,21 @@ func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName // NewTypeNameLazy returns a new defined type like NewTypeName, but it // lazily calls resolve to finish constructing the Named object. -func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { +func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName { obj := NewTypeName(pos, pkg, name, nil) - NewNamed(obj, nil, nil).resolve = resolve + + resolve := func(_ *Environment, t *Named) (*TypeParamList, Type, []*Func) { + tparams, underlying, methods := load(t) + + switch underlying.(type) { + case nil, *Named: + panic(fmt.Sprintf("invalid underlying type %T", t.underlying)) + } + + return bindTParams(tparams), underlying, methods + } + + NewNamed(obj, nil, nil).resolver = resolve return obj } diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index 009ac77012..e3186f5eed 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -210,7 +210,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams [] var err string switch T := rtyp.(type) { case *Named: - T.expand(nil) + T.resolve(check.conf.Environment) // The receiver type may be an instantiated type referred to // by an alias (which cannot have receiver parameters for now). if T.TypeArgs() != nil && sig.RecvTypeParams() == nil { diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 8d96494af0..87c1d7872b 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -179,13 +179,19 @@ func (subst *subster) typ(typ Type) Type { } } - if t.TypeParams().Len() == 0 { + // subst is called by expandNamed, so in this function we need to be + // careful not to call any methods that would cause t to be expanded: doing + // so would result in deadlock. + // + // So we call t.orig.TypeParams() rather than t.TypeParams() here and + // below. + if t.orig.TypeParams().Len() == 0 { dump(">>> %s is not parameterized", t) return t // type is not parameterized } var newTArgs []Type - assert(t.targs.Len() == t.TypeParams().Len()) + assert(t.targs.Len() == t.orig.TypeParams().Len()) // already instantiated dump(">>> %s already instantiated", t) @@ -198,7 +204,7 @@ func (subst *subster) typ(typ Type) Type { if new_targ != targ { dump(">>> substituted %d targ %s => %s", i, targ, new_targ) if newTArgs == nil { - newTArgs = make([]Type, t.TypeParams().Len()) + newTArgs = make([]Type, t.orig.TypeParams().Len()) copy(newTArgs, t.targs.list()) } newTArgs[i] = new_targ @@ -218,25 +224,22 @@ func (subst *subster) typ(typ Type) Type { return named } - // Create a new named type and populate the environment to avoid endless + t.orig.resolve(subst.env) + // Create a new instance and populate the environment to avoid endless // recursion. The position used here is irrelevant because validation only // occurs on t (we don't call validType on named), but we use subst.pos to // help with debugging. - tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil) - t.load() - // It's ok to provide a nil *Checker because the newly created type - // doesn't need to be (lazily) expanded; it's expanded below. - named := (*Checker)(nil).newNamed(tname, t.orig, nil, t.tparams, t.methods) // t is loaded, so tparams and methods are available - named.targs = NewTypeList(newTArgs) - subst.env.typeForHash(h, named) - t.expand(subst.env) // must happen after env update to avoid infinite recursion - - // do the substitution - dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTArgs) - named.underlying = subst.typOrNil(t.underlying) - dump(">>> underlying: %v", named.underlying) + named := subst.check.instance(subst.pos, t.orig, newTArgs, subst.env).(*Named) + // TODO(rfindley): we probably don't need to resolve here. Investigate if + // this can be removed. + named.resolve(subst.env) assert(named.underlying != nil) - named.fromRHS = named.underlying // for consistency, though no cycle detection is necessary + + // Note that if we were to expose substitution more generally (not just in + // the context of a declaration), we'd have to substitute in + // named.underlying as well. + // + // But this is unnecessary for now. return named diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 new file mode 100644 index 0000000000..4c4fc2fda8 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 @@ -0,0 +1,28 @@ +// Copyright 2021 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 p + +type Fooer[t any] interface { + foo(Barer[t]) +} +type Barer[t any] interface { + bar(Bazer[t]) +} +type Bazer[t any] interface { + Fooer[t] + baz(t) +} + +type Int int + +func (n Int) baz(int) {} +func (n Int) foo(b Barer[int]) { b.bar(n) } + +type F[t any] interface { f(G[t]) } +type G[t any] interface { g(H[t]) } +type H[t any] interface { F[t] } + +type T struct{} +func (n T) f(b G[T]) { b.g(n) } diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go index ca5ecdc434..400d6f7128 100644 --- a/src/cmd/compile/internal/types2/type.go +++ b/src/cmd/compile/internal/types2/type.go @@ -115,7 +115,7 @@ func asInterface(t Type) *Interface { func asNamed(t Type) *Named { e, _ := t.(*Named) if e != nil { - e.expand(nil) + e.resolve(nil) } return e } -- cgit v1.2.3-54-g00ecf From 4847c47cb8a93b56e1df8c249700e25f527d4ba3 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 14 Sep 2021 17:13:32 -0700 Subject: cmd/compile/internal/types2: eliminate Named.instPos This is a clean port of CL 349411 from go/types to types2. Change-Id: Id5fa04c53f286dad263d7ba7911cb49eebf47b0e Reviewed-on: https://go-review.googlesource.com/c/go/+/350030 Trust: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/environment.go | 7 ------- src/cmd/compile/internal/types2/errors.go | 2 +- src/cmd/compile/internal/types2/errors_test.go | 1 - src/cmd/compile/internal/types2/instantiate.go | 5 +++-- src/cmd/compile/internal/types2/named.go | 8 +++----- src/cmd/compile/internal/types2/sizeof_test.go | 2 +- src/cmd/compile/internal/types2/typestring.go | 10 ---------- 7 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/cmd/compile/internal/types2/environment.go b/src/cmd/compile/internal/types2/environment.go index 5ef8855a1b..fe9a3099fe 100644 --- a/src/cmd/compile/internal/types2/environment.go +++ b/src/cmd/compile/internal/types2/environment.go @@ -50,13 +50,6 @@ func (env *Environment) TypeHash(typ Type, targs []Type) string { h.typ(typ) } - if debug { - // there should be no instance markers in type hashes - for _, b := range buf.Bytes() { - assert(b != instanceMarker) - } - } - return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4 } diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go index a68273271b..ea43fab178 100644 --- a/src/cmd/compile/internal/types2/errors.go +++ b/src/cmd/compile/internal/types2/errors.go @@ -246,7 +246,7 @@ func stripAnnotations(s string) string { var b bytes.Buffer for _, r := range s { // strip #'s and subscript digits - if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080 + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 b.WriteRune(r) } } diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go index e1f0e83fc9..72a2ce3655 100644 --- a/src/cmd/compile/internal/types2/errors_test.go +++ b/src/cmd/compile/internal/types2/errors_test.go @@ -35,7 +35,6 @@ func TestStripAnnotations(t *testing.T) { {"foo", "foo"}, {"foo₀", "foo"}, {"foo(T₀)", "foo(T)"}, - {"#foo(T₀)", "foo(T)"}, } { got := stripAnnotations(test.in) if got != test.want { diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 5a6a13a107..7a9279943c 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -118,8 +118,9 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, env *Envi tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil) named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved named.targs = NewTypeList(targs) - named.instPos = &pos - named.resolver = expandNamed + named.resolver = func(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { + return expandNamed(env, n, pos) + } if env != nil { // It's possible that we've lost a race to add named to the environment. // In this case, use whichever instance is recorded in the environment. diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go index 7883b7347b..c844012e39 100644 --- a/src/cmd/compile/internal/types2/named.go +++ b/src/cmd/compile/internal/types2/named.go @@ -17,7 +17,6 @@ type Named struct { orig *Named // original, uninstantiated type fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting) underlying Type // possibly a *Named during setup; never a *Named once set up completely - instPos *syntax.Pos // position information for lazy instantiation, or nil tparams *TypeParamList // type parameters, or nil targs *TypeList // type arguments (after instantiation), or nil methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily @@ -222,11 +221,11 @@ func (n *Named) setUnderlying(typ Type) { // expandNamed ensures that the underlying type of n is instantiated. // The underlying type will be Typ[Invalid] if there was an error. -func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { +func expandNamed(env *Environment, n *Named, instPos syntax.Pos) (*TypeParamList, Type, []*Func) { n.orig.resolve(env) var u Type - if n.check.validateTArgLen(*n.instPos, n.orig.tparams.Len(), n.targs.Len()) { + if n.check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) { // TODO(rfindley): handling an optional Checker and Environment here (and // in subst) feels overly complicated. Can we simplify? if env == nil { @@ -245,11 +244,10 @@ func expandNamed(env *Environment, n *Named) (*TypeParamList, Type, []*Func) { // shouldn't return that instance from expand. env.typeForHash(h, n) } - u = n.check.subst(*n.instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) + u = n.check.subst(instPos, n.orig.underlying, makeSubstMap(n.orig.tparams.list(), n.targs.list()), env) } else { u = Typ[Invalid] } - n.instPos = nil return n.orig.tparams, u, n.orig.methods } diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go index bbaca8e0aa..a7f1185fa8 100644 --- a/src/cmd/compile/internal/types2/sizeof_test.go +++ b/src/cmd/compile/internal/types2/sizeof_test.go @@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) { {Interface{}, 44, 88}, {Map{}, 16, 32}, {Chan{}, 12, 24}, - {Named{}, 72, 136}, + {Named{}, 68, 128}, {TypeParam{}, 28, 48}, {term{}, 12, 24}, {top{}, 0, 0}, diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go index 71da37c3a1..bdafcf883d 100644 --- a/src/cmd/compile/internal/types2/typestring.go +++ b/src/cmd/compile/internal/types2/typestring.go @@ -63,9 +63,6 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { newTypeWriter(buf, qf).signature(sig) } -// instanceMarker is the prefix for an instantiated type in unexpanded form. -const instanceMarker = '#' - type typeWriter struct { buf *bytes.Buffer seen map[Type]bool @@ -245,13 +242,6 @@ func (w *typeWriter) typ(typ Type) { } case *Named: - // Instance markers indicate unexpanded instantiated - // types. Write them to aid debugging, but don't write - // them when we need an instance hash: whether a type - // is fully expanded or not doesn't matter for identity. - if w.env == nil && t.instPos != nil { - w.byte(instanceMarker) - } w.typePrefix(t) w.typeName(t.obj) if t.targs != nil { -- cgit v1.2.3-54-g00ecf From e4dfd788e66f4a44825598dd3cb6ca9626369228 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Tue, 14 Sep 2021 20:40:09 -0400 Subject: go/internal/gcimporter,cmd/compile: minor clean-up in iimport.go Make two superficial fixes to iimport.go: rename instType to instanceType (suggested in CL 349949), and fix a stale comment. Done in both go/internal/gcimporter and cmd/compile/internal/importer. Change-Id: Idfdda11a59b036a35824bbb1c101cba3652aeff4 Reviewed-on: https://go-review.googlesource.com/c/go/+/350031 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/importer/iimport.go | 6 +++--- src/go/internal/gcimporter/iimport.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go index 7f7143dcfe..a92720d52e 100644 --- a/src/cmd/compile/internal/importer/iimport.go +++ b/src/cmd/compile/internal/importer/iimport.go @@ -72,7 +72,7 @@ const ( structType interfaceType typeParamType - instType + instanceType unionType ) @@ -646,7 +646,7 @@ func (r *importReader) doType(base *types2.Named) types2.Type { r.p.doDecl(pkg, name) return r.p.tparamIndex[id] - case instType: + case instanceType: if r.p.exportVersion < iexportVersionGenerics { errorf("unexpected instantiation type") } @@ -661,7 +661,7 @@ func (r *importReader) doType(base *types2.Named) types2.Type { baseType := r.typ() // The imported instantiated type doesn't include any methods, so // we must always use the methods of the base (orig) type. - // TODO provide a non-nil *Checker + // TODO provide a non-nil *Environment t, _ := types2.Instantiate(nil, baseType, targs, false) return t diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go index d9174d470b..f570aab2bf 100644 --- a/src/go/internal/gcimporter/iimport.go +++ b/src/go/internal/gcimporter/iimport.go @@ -72,7 +72,7 @@ const ( structType interfaceType typeParamType - instType + instanceType unionType ) @@ -638,7 +638,7 @@ func (r *importReader) doType(base *types.Named) types.Type { r.p.doDecl(pkg, name) return r.p.tparamIndex[id] - case instType: + case instanceType: if r.p.exportVersion < iexportVersionGenerics { errorf("unexpected instantiation type") } @@ -653,7 +653,7 @@ func (r *importReader) doType(base *types.Named) types.Type { baseType := r.typ() // The imported instantiated type doesn't include any methods, so // we must always use the methods of the base (orig) type. - // TODO provide a non-nil *Checker + // TODO provide a non-nil *Environment t, _ := types.Instantiate(nil, baseType, targs, false) return t -- cgit v1.2.3-54-g00ecf From 5b48fca1fad44d22105f64be725514020432a2c1 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Fri, 3 Sep 2021 15:04:52 +0200 Subject: cmd/compile: mark wrapper functions with DW_AT_trampoline Change DWARF generation to tag wrapper functions with the "DW_AT_trampoline attribute". The intent is that debuggers can pick up on this attr so as to skip through the wrapper to the eventual target. DWARF standard allows for a couple of different possible variants of the trampoline attr; this is the simplest variant (all it tells the debugger is that the function is a wrapper, doesn't include a reference to the wrapper routine). This implementation keys off the WRAPPER LSym attribute, which is set for method wrappers, ABI wrappers, and a selected set of runtime assembly routines (ex: "runtime.call32"). Change-Id: Ib53e1bc56c02b86ca3ac5e7da1a541ec262726cf Reviewed-on: https://go-review.googlesource.com/c/go/+/347352 Reviewed-by: Than McIntosh Run-TryBot: Than McIntosh TryBot-Result: Go Bot Trust: Jeremy Faller --- src/cmd/internal/dwarf/dwarf.go | 61 ++++++++++++++++++++++++++++++++++------- src/cmd/internal/obj/dwarf.go | 4 +-- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 4e163db020..69aafaf986 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -325,8 +325,10 @@ const ( DW_ABRV_COMPUNIT DW_ABRV_COMPUNIT_TEXTLESS DW_ABRV_FUNCTION + DW_ABRV_WRAPPER DW_ABRV_FUNCTION_ABSTRACT DW_ABRV_FUNCTION_CONCRETE + DW_ABRV_WRAPPER_CONCRETE DW_ABRV_INLINED_SUBROUTINE DW_ABRV_INLINED_SUBROUTINE_RANGES DW_ABRV_VARIABLE @@ -455,6 +457,19 @@ var abbrevs = [DW_NABRV]dwAbbrev{ }, }, + /* WRAPPER */ + { + DW_TAG_subprogram, + DW_CHILDREN_yes, + []dwAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_low_pc, DW_FORM_addr}, + {DW_AT_high_pc, DW_FORM_addr}, + {DW_AT_frame_base, DW_FORM_block1}, + {DW_AT_trampoline, DW_FORM_flag}, + }, + }, + /* FUNCTION_ABSTRACT */ { DW_TAG_subprogram, @@ -478,6 +493,19 @@ var abbrevs = [DW_NABRV]dwAbbrev{ }, }, + /* WRAPPER_CONCRETE */ + { + DW_TAG_subprogram, + DW_CHILDREN_yes, + []dwAttrForm{ + {DW_AT_abstract_origin, DW_FORM_ref_addr}, + {DW_AT_low_pc, DW_FORM_addr}, + {DW_AT_high_pc, DW_FORM_addr}, + {DW_AT_frame_base, DW_FORM_block1}, + {DW_AT_trampoline, DW_FORM_flag}, + }, + }, + /* INLINED_SUBROUTINE */ { DW_TAG_inlined_subroutine, @@ -1329,11 +1357,14 @@ func putInlinedFunc(ctxt Context, s *FnState, callIdx int) error { // for the function (which holds location-independent attributes such // as name, type), then the remainder of the attributes are specific // to this instance (location, frame base, etc). -func PutConcreteFunc(ctxt Context, s *FnState) error { +func PutConcreteFunc(ctxt Context, s *FnState, isWrapper bool) error { if logDwarf { ctxt.Logf("PutConcreteFunc(%v)\n", s.Info) } abbrev := DW_ABRV_FUNCTION_CONCRETE + if isWrapper { + abbrev = DW_ABRV_WRAPPER_CONCRETE + } Uleb128put(ctxt, s.Info, int64(abbrev)) // Abstract origin. @@ -1346,6 +1377,10 @@ func PutConcreteFunc(ctxt Context, s *FnState) error { // cfa / frame base putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa}) + if isWrapper { + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0) + } + // Scopes if err := putPrunedScopes(ctxt, s, abbrev); err != nil { return err @@ -1368,11 +1403,14 @@ func PutConcreteFunc(ctxt Context, s *FnState) error { // when its containing package was compiled (hence there is no need to // emit an abstract version for it to use as a base for inlined // routine records). -func PutDefaultFunc(ctxt Context, s *FnState) error { +func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error { if logDwarf { ctxt.Logf("PutDefaultFunc(%v)\n", s.Info) } abbrev := DW_ABRV_FUNCTION + if isWrapper { + abbrev = DW_ABRV_WRAPPER + } Uleb128put(ctxt, s.Info, int64(abbrev)) // Expand '"".' to import path. @@ -1385,13 +1423,16 @@ func PutDefaultFunc(ctxt Context, s *FnState) error { putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, 0, s.StartPC) putattr(ctxt, s.Info, abbrev, DW_FORM_addr, DW_CLS_ADDRESS, s.Size, s.StartPC) putattr(ctxt, s.Info, abbrev, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa}) - ctxt.AddFileRef(s.Info, s.Filesym) - - var ev int64 - if s.External { - ev = 1 + if isWrapper { + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, int64(1), 0) + } else { + ctxt.AddFileRef(s.Info, s.Filesym) + var ev int64 + if s.External { + ev = 1 + } + putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0) } - putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, ev, 0) // Scopes if err := putPrunedScopes(ctxt, s, abbrev); err != nil { @@ -1489,10 +1530,10 @@ func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) { // Determine whether to use a concrete variable or regular variable DIE. concrete := true switch fnabbrev { - case DW_ABRV_FUNCTION: + case DW_ABRV_FUNCTION, DW_ABRV_WRAPPER: concrete = false break - case DW_ABRV_FUNCTION_CONCRETE: + case DW_ABRV_FUNCTION_CONCRETE, DW_ABRV_WRAPPER_CONCRETE: // If we're emitting a concrete subprogram DIE and the variable // in question is not part of the corresponding abstract function DIE, // then use the default (non-concrete) abbrev for this param. diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 6dd53ffd12..29e367aa4c 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -378,9 +378,9 @@ func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) if err != nil { ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) } - err = dwarf.PutConcreteFunc(dwctxt, fnstate) + err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper()) } else { - err = dwarf.PutDefaultFunc(dwctxt, fnstate) + err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper()) } if err != nil { ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) -- cgit v1.2.3-54-g00ecf From 72bb8185b5fb2fe84ee7cfdc8e9605f2c81b32fe Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Wed, 25 Aug 2021 10:08:07 +0200 Subject: cmd/compile: emit DWARF info about dictionary entries When emitting the DIE of the instantiation of a generic function also emit one DW_TAG_typedef_type entry for each dictionary entry in use, referencing the shape type and having a custom attribute containing the index inside the dictionary. When emitting the DIE of variables that have an instantiated parametric type, instead of referencing the shape type directly go through the DW_TAG_typedef_type entry emitted for the dictionary entry describing the real type of the variable. Change-Id: Ia45d157ecd4c25e2b60300469e43bbb27a663582 Reviewed-on: https://go-review.googlesource.com/c/go/+/344929 Run-TryBot: Alessandro Arzilli Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Dan Scales Reviewed-by: Than McIntosh Trust: Dan Scales Trust: Jeremy Faller --- src/cmd/compile/internal/dwarfgen/dwarf.go | 3 ++ src/cmd/compile/internal/ir/name.go | 1 + src/cmd/compile/internal/noder/stencil.go | 18 +++++-- src/cmd/internal/dwarf/dwarf.go | 67 +++++++++++++++++++++++- src/cmd/link/internal/ld/dwarf_test.go | 84 ++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 5 deletions(-) diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 30472a9ebd..3007262db9 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -217,6 +217,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, }) // Record go type of to insure that it gets emitted by the linker. fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type())) @@ -374,6 +375,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, } } @@ -478,6 +480,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var DeclCol: declpos.RelCol(), InlIndex: int32(inlIndex), ChildIndex: -1, + DictIndex: n.DictIndex, } list := debug.LocationLists[varID] if len(list) != 0 { diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go index 9fb22378cd..dcfff7deba 100644 --- a/src/cmd/compile/internal/ir/name.go +++ b/src/cmd/compile/internal/ir/name.go @@ -40,6 +40,7 @@ type Name struct { Class Class // uint8 pragma PragmaFlag // int16 flags bitset16 + DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1 sym *types.Sym Func *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem Offset_ int64 diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 5069db9fe1..6c990c1828 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -779,6 +779,7 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name { m.Func = name.Func subst.ts.Vars[name] = m m.SetTypecheck(1) + m.DictIndex = name.DictIndex if name.Defn != nil { if name.Defn.Op() == ir.ONAME { // This is a closure variable, so its Defn is the outer @@ -1268,14 +1269,18 @@ func (subst *subster) node(n ir.Node) ir.Node { // function info.gfInfo. This will indicate the dictionary entry with the // correct concrete type for the associated instantiated function. func findDictType(info *instInfo, t *types.Type) int { - for i, dt := range info.gfInfo.tparams { + return info.gfInfo.findDictType(t) +} + +func (gfInfo *gfInfo) findDictType(t *types.Type) int { + for i, dt := range gfInfo.tparams { if dt == t { return i } } - for i, dt := range info.gfInfo.derivedTypes { + for i, dt := range gfInfo.derivedTypes { if types.Identical(dt, t) { - return i + len(info.gfInfo.tparams) + return i + len(gfInfo.tparams) } } return -1 @@ -1736,6 +1741,7 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { for _, n := range gf.Dcl { addType(&info, n, n.Type()) + n.DictIndex = uint16(info.findDictType(n.Type()) + 1) } if infoPrintMode { @@ -1805,9 +1811,13 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { // Visit the closure body and add all relevant entries to the // dictionary of the outer function (closure will just use // the dictionary of the outer function). - for _, n1 := range n.(*ir.ClosureExpr).Func.Body { + cfunc := n.(*ir.ClosureExpr).Func + for _, n1 := range cfunc.Body { ir.Visit(n1, visitFunc) } + for _, n := range cfunc.Dcl { + n.DictIndex = uint16(info.findDictType(n.Type()) + 1) + } } if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() { for _, cc := range n.(*ir.SwitchStmt).Cases { diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 69aafaf986..be37641706 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -50,6 +50,7 @@ type Var struct { Abbrev int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST] IsReturnValue bool IsInlFormal bool + DictIndex uint16 // index of the dictionary entry describing the type of this variable StackOffset int32 // This package can't use the ssa package, so it can't mention ssa.FuncDebug, // so indirect through a closure. @@ -97,6 +98,8 @@ type FnState struct { Scopes []Scope InlCalls InlCalls UseBASEntries bool + + dictIndexToOffset []int64 } func EnableLogging(doit bool) { @@ -315,6 +318,7 @@ const ( DW_AT_go_runtime_type = 0x2904 DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit + DW_AT_go_dict_index = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape DW_AT_internal_location = 253 // params and locals; not emitted ) @@ -362,6 +366,7 @@ const ( DW_ABRV_STRINGTYPE DW_ABRV_STRUCTTYPE DW_ABRV_TYPEDECL + DW_ABRV_DICT_INDEX DW_NABRV ) @@ -882,6 +887,17 @@ var abbrevs = [DW_NABRV]dwAbbrev{ {DW_AT_type, DW_FORM_ref_addr}, }, }, + + /* DICT_INDEX */ + { + DW_TAG_typedef, + DW_CHILDREN_no, + []dwAttrForm{ + {DW_AT_name, DW_FORM_string}, + {DW_AT_type, DW_FORM_ref_addr}, + {DW_AT_go_dict_index, DW_FORM_udata}, + }, + }, } // GetAbbrev returns the contents of the .debug_abbrev section. @@ -1196,6 +1212,9 @@ func putPrunedScopes(ctxt Context, s *FnState, fnabbrev int) error { sort.Sort(byChildIndex(pruned.Vars)) scopes[k] = pruned } + + s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev) + var encbuf [20]byte if putscope(ctxt, s, scopes, 0, fnabbrev, encbuf[:0]) < int32(len(scopes)) { return errors.New("multiple toplevel scopes") @@ -1451,6 +1470,47 @@ func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error { return nil } +// putparamtypes writes typedef DIEs for any parametric types that are used by this function. +func putparamtypes(ctxt Context, s *FnState, scopes []Scope, fnabbrev int) []int64 { + if fnabbrev == DW_ABRV_FUNCTION_CONCRETE { + return nil + } + + maxDictIndex := uint16(0) + + for i := range scopes { + for _, v := range scopes[i].Vars { + if v.DictIndex > maxDictIndex { + maxDictIndex = v.DictIndex + } + } + } + + if maxDictIndex == 0 { + return nil + } + + dictIndexToOffset := make([]int64, maxDictIndex) + + for i := range scopes { + for _, v := range scopes[i].Vars { + if v.DictIndex == 0 || dictIndexToOffset[v.DictIndex-1] != 0 { + continue + } + + dictIndexToOffset[v.DictIndex-1] = ctxt.CurrentOffset(s.Info) + + Uleb128put(ctxt, s.Info, int64(DW_ABRV_DICT_INDEX)) + n := fmt.Sprintf(".param%d", v.DictIndex-1) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DictIndex-1), nil) + } + } + + return dictIndexToOffset +} + func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev int, encbuf []byte) int32 { if logDwarf { @@ -1624,7 +1684,12 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil) } putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) - putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 { + // If the type of this variable is parametric use the entry emitted by putparamtypes + putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info) + } else { + putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) + } } if abbrevUsesLoclist(abbrev) { diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 0aeaa7565c..3d112d97a4 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -11,6 +11,7 @@ import ( "debug/pe" "errors" "fmt" + "internal/buildcfg" "internal/testenv" "io" "io/ioutil" @@ -1746,3 +1747,86 @@ func main() { expected, found) } } + +func TestDictIndex(t *testing.T) { + // Check that variables with a parametric type have a dictionary index attribute + testenv.MustHaveGoBuild(t) + + if runtime.GOOS == "plan9" { + t.Skip("skipping on plan9; no DWARF symbol table in executables") + } + if buildcfg.Experiment.Unified { + t.Skip("GOEXPERIMENT=unified does not emit dictionaries yet") + } + t.Parallel() + + const prog = ` +package main + +import "fmt" + +func testfn[T any](arg T) { + var mapvar = make(map[int]T) + mapvar[0] = arg + fmt.Println(arg, mapvar) +} + +func main() { + testfn("test") +} +` + + dir := t.TempDir() + f := gobuild(t, dir, prog, NoOpt) + defer f.Close() + + d, err := f.DWARF() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + + rdr := d.Reader() + found := false + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + name, _ := entry.Val(dwarf.AttrName).(string) + if strings.HasPrefix(name, "main.testfn") { + found = true + break + } + } + + if !found { + t.Fatalf("could not find main.testfn") + } + + offs := []dwarf.Offset{} + for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if entry.Tag == 0 { + break + } + name, _ := entry.Val(dwarf.AttrName).(string) + switch name { + case "arg", "mapvar": + offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset)) + } + } + if len(offs) != 2 { + t.Errorf("wrong number of variables found in main.testfn %d", len(offs)) + } + for _, off := range offs { + rdr.Seek(off) + entry, err := rdr.Next() + if err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok { + t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index)) + } + } +} -- cgit v1.2.3-54-g00ecf From 6196979365ec6b527b3731c9ec13d7ddfe429f86 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Tue, 24 Aug 2021 11:51:07 -0700 Subject: cmd/go/internal/modload: prevent tidy downgrading disambiguating modules If an indirectly required module does not provide any packages needed to build packages in the main module but is needed to disambiguate imports, 'go mod tidy' may keep an indirect requirement on that module to prevent it from being downgraded. This can prevent the introduction of new ambiguities. This also ensures tidy keeps sums needed to load all packages. Fixes #47738 Change-Id: Ib8e33422b95394707894cda7cfbb71a4b111e0ed Reviewed-on: https://go-review.googlesource.com/c/go/+/344572 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Bryan C. Mills --- src/cmd/go/internal/modload/buildlist.go | 68 +++++++++++++++++++--- src/cmd/go/internal/modload/import.go | 46 ++++++++------- src/cmd/go/internal/modload/load.go | 12 ++-- .../script/mod_tidy_downgrade_ambiguous.txt | 58 ++++++++++++++++++ 4 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index 777e29af10..da9e6406b1 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -591,7 +591,7 @@ func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, // selected at the same version or is upgraded by the dependencies of a // root. // -// If any module that provided a package has been upgraded above its previous, +// If any module that provided a package has been upgraded above its previous // version, the caller may need to reload and recompute the package graph. // // To ensure that the loading process eventually converges, the caller should @@ -980,17 +980,37 @@ func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Versi return true } -// tidyUnprunedRoots returns a minimal set of root requirements that maintains the -// selected version of every module that provided a package in pkgs, and -// includes the selected version of every such module in direct as a root. +// tidyUnprunedRoots returns a minimal set of root requirements that maintains +// the selected version of every module that provided or lexically could have +// provided a package in pkgs, and includes the selected version of every such +// module in direct as a root. func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) { var ( + // keep is a set of of modules that provide packages or are needed to + // disambiguate imports. keep []module.Version keptPath = map[string]bool{} - ) - var ( - rootPaths []string // module paths that should be included as roots + + // rootPaths is a list of module paths that provide packages directly + // imported from the main module. They should be included as roots. + rootPaths []string inRootPaths = map[string]bool{} + + // altMods is a set of paths of modules that lexically could have provided + // imported packages. It may be okay to remove these from the list of + // explicit requirements if that removes them from the module graph. If they + // are present in the module graph reachable from rootPaths, they must not + // be at a lower version. That could cause a missing sum error or a new + // import ambiguity. + // + // For example, suppose a developer rewrites imports from example.com/m to + // example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the + // requirement on example.com/m if there is no other transitive requirement + // on it. However, if example.com/m were downgraded to a version not in + // go.sum, when package example.com/m/v2/p is loaded, we'd get an error + // trying to disambiguate the import, since we can't check example.com/m + // without its sum. See #47738. + altMods = map[string]string{} ) for _, pkg := range pkgs { if !pkg.fromExternalModule() { @@ -1004,12 +1024,44 @@ func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct ma inRootPaths[m.Path] = true } } + for _, m := range pkg.altMods { + altMods[m.Path] = m.Version + } } - min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep}) + // Construct a build list with a minimal set of roots. + // This may remove or downgrade modules in altMods. + reqs := &mvsReqs{roots: keep} + min, err := mvs.Req(mainModule, rootPaths, reqs) + if err != nil { + return nil, err + } + buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs) if err != nil { return nil, err } + + // Check if modules in altMods were downgraded but not removed. + // If so, add them to roots, which will retain an "// indirect" requirement + // in go.mod. See comment on altMods above. + keptAltMod := false + for _, m := range buildList { + if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 { + keep = append(keep, module.Version{Path: m.Path, Version: v}) + keptAltMod = true + } + } + if keptAltMod { + // We must run mvs.Req again instead of simply adding altMods to min. + // It's possible that a requirement in altMods makes some other + // explicit indirect requirement unnecessary. + reqs.roots = keep + min, err = mvs.Req(mainModule, rootPaths, reqs) + if err != nil { + return nil, err + } + } + return newRequirements(unpruned, min, direct), nil } diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go index de47974b9b..e64677acd0 100644 --- a/src/cmd/go/internal/modload/import.go +++ b/src/cmd/go/internal/modload/import.go @@ -243,20 +243,24 @@ func (e *invalidImportError) Unwrap() error { // // If the package is not present in any module selected from the requirement // graph, importFromModules returns an *ImportMissingError. -func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) { +// +// If the package is present in exactly one module, importFromModules will +// return the module, its root directory, and a list of other modules that +// lexically could have provided the package but did not. +func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) { if strings.Contains(path, "@") { - return module.Version{}, "", fmt.Errorf("import path should not have @version") + return module.Version{}, "", nil, fmt.Errorf("import path should not have @version") } if build.IsLocalImport(path) { - return module.Version{}, "", fmt.Errorf("relative import not supported") + return module.Version{}, "", nil, fmt.Errorf("relative import not supported") } if path == "C" { // There's no directory for import "C". - return module.Version{}, "", nil + return module.Version{}, "", nil, nil } // Before any further lookup, check that the path is valid. if err := module.CheckImportPath(path); err != nil { - return module.Version{}, "", &invalidImportError{importPath: path, err: err} + return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err} } // Is the package in the standard library? @@ -265,14 +269,14 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M for _, mainModule := range MainModules.Versions() { if MainModules.InGorootSrc(mainModule) { if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil { - return module.Version{}, dir, err + return module.Version{}, dir, nil, err } else if ok { - return mainModule, dir, nil + return mainModule, dir, nil, nil } } } dir := filepath.Join(cfg.GOROOT, "src", path) - return module.Version{}, dir, nil + return module.Version{}, dir, nil, nil } // -mod=vendor is special. @@ -283,19 +287,19 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true) vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false) if mainOK && vendorOK { - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} + return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}} } // Prefer to return main directory if there is one, // Note that we're not checking that the package exists. // We'll leave that for load. if !vendorOK && mainDir != "" { - return mainModule, mainDir, nil + return mainModule, mainDir, nil, nil } if mainErr != nil { - return module.Version{}, "", mainErr + return module.Version{}, "", nil, mainErr } readVendorList(mainModule) - return vendorPkgModule[path], vendorDir, nil + return vendorPkgModule[path], vendorDir, nil, nil } // Check each module on the build list. @@ -316,7 +320,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // already non-nil, then we attempt to load the package using the full // requirements in mg. for { - var sumErrMods []module.Version + var sumErrMods, altMods []module.Version for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) { var ( v string @@ -350,13 +354,15 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // continue the loop and find the package in some other module, // we need to look at this module to make sure the import is // not ambiguous. - return module.Version{}, "", err + return module.Version{}, "", nil, err } if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil { - return module.Version{}, "", err + return module.Version{}, "", nil, err } else if ok { mods = append(mods, m) dirs = append(dirs, dir) + } else { + altMods = append(altMods, m) } } @@ -369,7 +375,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M mods[i], mods[j] = mods[j], mods[i] dirs[i], dirs[j] = dirs[j], dirs[i] } - return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} + return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods} } if len(sumErrMods) > 0 { @@ -377,7 +383,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M j := len(sumErrMods) - 1 - i sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i] } - return module.Version{}, "", &ImportMissingSumError{ + return module.Version{}, "", nil, &ImportMissingSumError{ importPath: path, mods: sumErrMods, found: len(mods) > 0, @@ -385,7 +391,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M } if len(mods) == 1 { - return mods[0], dirs[0], nil + return mods[0], dirs[0], altMods, nil } if mg != nil { @@ -395,7 +401,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M if !HasModRoot() { queryErr = ErrNoModRoot } - return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} + return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd} } // So far we've checked the root dependencies. @@ -406,7 +412,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M // the module graph, so we can't return an ImportMissingError here — one // of the missing modules might actually contain the package in question, // in which case we shouldn't go looking for it in some new dependency. - return module.Version{}, "", err + return module.Version{}, "", nil, err } } } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 40e6b50ed4..48f268ce5f 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -862,6 +862,7 @@ type loadPkg struct { imports []*loadPkg // packages imported by this one testImports []string // test-only imports, saved for use by pkg.test. inStd bool + altMods []module.Version // modules that could have contained the package but did not // Populated by (*loader).pkgTest: testOnce sync.Once @@ -1184,8 +1185,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader { } // updateRequirements ensures that ld.requirements is consistent with the -// information gained from ld.pkgs and includes the modules in add as roots at -// at least the given versions. +// information gained from ld.pkgs. // // In particular: // @@ -1343,7 +1343,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err // // In some sense, we can think of this as ‘upgraded the module providing // pkg.path from "none" to a version higher than "none"’. - if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { + if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil { changed = true break } @@ -1554,7 +1554,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch // If the main module is tidy and the package is in "all" — or if we're // lucky — we can identify all of its imports without actually loading the // full module graph. - m, _, err := importFromModules(ctx, path, ld.requirements, nil) + m, _, _, err := importFromModules(ctx, path, ld.requirements, nil) if err != nil { var missing *ImportMissingError if errors.As(err, &missing) && ld.ResolveMissingImports { @@ -1659,7 +1659,7 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) { } } - pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) + pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg) if pkg.dir == "" { return } @@ -1918,7 +1918,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements) pkg := pkg ld.work.Add(func() { - mod, _, err := importFromModules(ctx, pkg.path, rs, mg) + mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg) if mod != pkg.mod { mismatches := <-mismatchMu mismatches[pkg] = mismatch{mod: mod, err: err} diff --git a/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt new file mode 100644 index 0000000000..8b508c7ea8 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt @@ -0,0 +1,58 @@ +# Verifies golang.org/issue/47738. + +# In this test, the user has rewritten their imports to use rsc.io/quote/v3, +# but their go.mod still requires rsc.io/quote@v1.5.2, and they indirectly +# require rsc.io/quote@v1.5.1 but don't import anything from it. +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2$' +! stdout 'rsc.io/quote/v3' +go list -e all +! stdout '^rsc.io/quote$' + +# 'go mod tidy' should preserve the requirement on rsc.io/quote but mark it +# indirect. This prevents a downgrade to v1.5.1, which could introduce +# an ambiguity. +go mod tidy +go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all +stdout '^rsc.io/quote@v1.5.2 indirect$' +stdout '^rsc.io/quote/v3@v3.0.0$' + +-- go.mod -- +module use + +go 1.16 + +require ( + old-indirect v0.0.0 + rsc.io/quote v1.5.2 +) + +replace old-indirect v0.0.0 => ./old-indirect +-- go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.1/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +-- use.go -- +package use + +import ( + _ "old-indirect/empty" + + _ "rsc.io/quote/v3" +) +-- old-indirect/empty/empty.go -- +package empty +-- old-indirect/go.mod -- +module old-indirect + +go 1.16 + +require rsc.io/quote v1.5.1 +-- old-indirect/go.sum -- +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -- cgit v1.2.3-54-g00ecf From 03df68d3c33e83a23cf5f22389a37f2d09721bef Mon Sep 17 00:00:00 2001 From: nimelehin Date: Wed, 15 Sep 2021 18:37:08 +0000 Subject: runtime: fix setting of cpu features for amd64 Because of wrong case of letters, the cpu features flags were not set properly for amd64. Fixes #48406. Change-Id: If19782851670e91fd31d119f4701c47373fa7e71 GitHub-Last-Rev: 91c7321ca49343c86917f071babec8a004ca5c77 GitHub-Pull-Request: golang/go#48403 Reviewed-on: https://go-review.googlesource.com/c/go/+/350151 Trust: Keith Randall Reviewed-by: Austin Clements Run-TryBot: Austin Clements TryBot-Result: Go Bot --- src/runtime/proc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 197441dfa7..605e133000 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -623,7 +623,7 @@ func cpuinit() { // Support cpu feature variables are used in code generated by the compiler // to guard execution of instructions that can not be assumed to be always supported. switch GOARCH { - case "386", "AMD64": + case "386", "amd64": x86HasPOPCNT = cpu.X86.HasPOPCNT x86HasSSE41 = cpu.X86.HasSSE41 x86HasFMA = cpu.X86.HasFMA -- cgit v1.2.3-54-g00ecf From 0edc6c4fa088a74bef98d55cc93ffa387d4f7b2d Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Mon, 16 Aug 2021 09:37:07 -0500 Subject: cmd/internal/obj/ppc64: generate prologue code compatible with new ABI This changes the ppc64 prologue to avoid clobbering the registers that could contain incoming argument values. This means preserving the values in R3 - R10 and R14 - R19 for ppc64. Instead of modifying R3, R4, R5 and R6 the registers R22, R23, R24 and R25 are used. The argument registers that could be clobbered by the call to morestack are saved and restored around that call. Change-Id: If354c3dc73f2c8537ef4e513e5a4c05d7bd22866 Reviewed-on: https://go-review.googlesource.com/c/go/+/343872 Trust: Lynn Boger Run-TryBot: Lynn Boger TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/internal/obj/ppc64/obj9.go | 63 +++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go index c2722b0afb..ee93fe048b 100644 --- a/src/cmd/internal/obj/ppc64/obj9.go +++ b/src/cmd/internal/obj/ppc64/obj9.go @@ -294,9 +294,9 @@ func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { // BL (LR) var sym *obj.LSym if p.As == obj.ADUFFZERO { - sym = c.ctxt.Lookup("runtime.duffzero") + sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal) } else { - sym = c.ctxt.Lookup("runtime.duffcopy") + sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal) } offset := p.To.Offset p.As = AMOVD @@ -687,7 +687,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Reg = REG_LR q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP - prologueEnd = q q = obj.Appendp(q, c.newprog) @@ -787,14 +786,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Reg = REGG q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R3 + q.To.Reg = REG_R22 q = obj.Appendp(q, c.newprog) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R0 q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R3 + q.To.Reg = REG_R22 q = obj.Appendp(q, c.newprog) q.As = ABEQ @@ -804,10 +803,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q = obj.Appendp(q, c.newprog) q.As = AMOVD q.From.Type = obj.TYPE_MEM - q.From.Reg = REG_R3 + q.From.Reg = REG_R22 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R4 + q.To.Reg = REG_R23 q = obj.Appendp(q, c.newprog) q.As = AADD @@ -815,14 +814,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R5 + q.To.Reg = REG_R24 q = obj.Appendp(q, c.newprog) q.As = ACMP q.From.Type = obj.TYPE_REG - q.From.Reg = REG_R4 + q.From.Reg = REG_R23 q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R5 + q.To.Reg = REG_R24 q = obj.Appendp(q, c.newprog) q.As = ABNE @@ -835,14 +834,14 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { q.From.Offset = c.ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG - q.To.Reg = REG_R6 + q.To.Reg = REG_R25 q = obj.Appendp(q, c.newprog) q.As = AMOVD q.From.Type = obj.TYPE_REG - q.From.Reg = REG_R6 + q.From.Reg = REG_R25 q.To.Type = obj.TYPE_MEM - q.To.Reg = REG_R3 + q.To.Reg = REG_R22 q.To.Offset = 0 // Panic.argp q = obj.Appendp(q, c.newprog) @@ -1051,7 +1050,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode - // MOVD g_stackguard(g), R3 + // MOVD g_stackguard(g), R22 p = obj.Appendp(p, c.newprog) p.As = AMOVD @@ -1062,7 +1061,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 } p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R3 + p.To.Reg = REG_R22 // Mark the stack bound check and morestack call async nonpreemptible. // If we get preempted here, when resumed the preemption request is @@ -1078,7 +1077,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.As = ACMPU p.From.Type = obj.TYPE_REG - p.From.Reg = REG_R3 + p.From.Reg = REG_R22 p.To.Type = obj.TYPE_REG p.To.Reg = REGSP } else { @@ -1108,14 +1107,14 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Type = obj.TYPE_CONST p.From.Offset = offset p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R23 p = obj.Appendp(p, c.newprog) p.As = ACMPU p.From.Type = obj.TYPE_REG p.From.Reg = REGSP p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R23 } p = obj.Appendp(p, c.newprog) @@ -1134,14 +1133,14 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.From.Offset = -offset p.Reg = REGSP p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R23 p = obj.Appendp(p, c.newprog) p.As = ACMPU p.From.Type = obj.TYPE_REG - p.From.Reg = REG_R3 + p.From.Reg = REG_R22 p.To.Type = obj.TYPE_REG - p.To.Reg = REG_R4 + p.To.Reg = REG_R23 } // q1: BLT done @@ -1151,17 +1150,25 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.As = ABLT p.To.Type = obj.TYPE_BRANCH - // MOVD LR, R5 p = obj.Appendp(p, c.newprog) + p.As = obj.ANOP // zero-width place holder + + if q != nil { + q.To.SetTarget(p) + } + + // Spill the register args that could be clobbered by the + // morestack code. + spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog) + + // MOVD LR, R5 + p = obj.Appendp(spill, c.newprog) p.As = AMOVD p.From.Type = obj.TYPE_REG p.From.Reg = REG_LR p.To.Type = obj.TYPE_REG p.To.Reg = REG_R5 - if q != nil { - q.To.SetTarget(p) - } p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) @@ -1181,8 +1188,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in // the caller's frame, but not used (0(SP) is caller's saved LR, // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2. - - // MOVD R12, 8(SP) + // MOVD R2, 8(SP) p = obj.Appendp(p, c.newprog) p.As = AMOVD p.From.Type = obj.TYPE_REG @@ -1249,7 +1255,8 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { p.To.Reg = REG_R2 } - p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) + unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog) + p = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1) // BR start p = obj.Appendp(p, c.newprog) -- cgit v1.2.3-54-g00ecf From 59a9a035ffa34c26a287d124180f6eca7c912311 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 13 Sep 2021 13:53:18 -0700 Subject: cmd/compile: switch to computing dict format on instantiated functions Change to computing the dictionary form on each shape-instantiated function, rather than once on the underlying generic function/method. The problem with computing the dictionary format on the generic function is that we had to force early transformations to create all the needed/implicit CONVIFACE nodes, since many of these nodes cause the need for a dictionary entry. Also, the dictionary entries needed can different with different instantiations of the same generic function, especially depending on whether a type argument is a non-interface or interface type, or a instantiated type vs. a non-instantiated type. By computing the dictionary format on the instantiated function, we are scanning a function where all the transformations have been done to create implicit CONVFIFACE nodes, and we know the above relevant information about the type params (which are shapes). Much of the change is more mechanical changes from typeparams to shapes, and generic functions/info to instantiated functions/info. Some of the most important non-mechanical changes are: - Separated out the dictionary transformations to nodes into a separate dictPass, since we need to analyze instantiated functions after stenciling, but before the dictionary transformations. - Added type param index to shape types, since we need to be able distinguish type params of an instantiation which are different but happen to have the same shape. - Allow the type substituter to work with shapes again (since for the dictionary entries we need to substitute shape params to the concrete type args). - Support types.IdentityStrict() that does strict type comparison (no special case for shapes). This needed for type substitution, formatting and creating dictionaries, etc. We can maybe create better names for this function. - Add new information to instInfo to contain a mapping from the shape type params to their instantiated type bound. This is needed when doing the dictionary transformations related to type bounds. Change-Id: I1c3ca312c5384f318c4dd7d0858dba9766396ff6 Reviewed-on: https://go-review.googlesource.com/c/go/+/349613 Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/irgen.go | 57 ++- src/cmd/compile/internal/noder/stencil.go | 534 +++++++++++++----------- src/cmd/compile/internal/reflectdata/reflect.go | 2 +- src/cmd/compile/internal/typecheck/subr.go | 59 +-- src/cmd/compile/internal/types/identity.go | 31 +- 5 files changed, 375 insertions(+), 308 deletions(-) diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go index 414875615f..4f1b4e6bfd 100644 --- a/src/cmd/compile/internal/noder/irgen.go +++ b/src/cmd/compile/internal/noder/irgen.go @@ -97,39 +97,42 @@ func check2(noders []*noder) { } } -// gfInfo is information gathered on a generic function. -type gfInfo struct { - tparams []*types.Type +// dictInfo is the dictionary format for an instantiation of a generic function with +// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe +// the actual dictionary entries in order, and the remaining fields are other info +// needed in doing dictionary processing during compilation. +type dictInfo struct { + // Types substituted for the type parameters, which are shape types. + shapeParams []*types.Type + // All types derived from those typeparams used in the instantiation. derivedTypes []*types.Type - // Nodes in generic function that requires a subdictionary. Includes + // Nodes in the instantiation that requires a subdictionary. Includes // method and function calls (OCALL), function values (OFUNCINST), method // values/expressions (OXDOT). subDictCalls []ir.Node - // Nodes in generic functions that are a conversion from a typeparam/derived + // Nodes in the instantiation that are a conversion from a typeparam/derived // type to a specific interface. itabConvs []ir.Node + + // Mapping from each shape type that substitutes a type param, to its + // type bound (which is also substitued with shapes if it is parameterized) + shapeToBound map[*types.Type]*types.Type + // For type switches on nonempty interfaces, a map from OTYPE entries of - // HasTParam type, to the interface type we're switching from. - // TODO: what if the type we're switching from is a shape type? + // HasShape type, to the interface type we're switching from. type2switchType map[ir.Node]*types.Type + + startSubDict int // Start of dict entries for subdictionaries + startItabConv int // Start of dict entries for itab conversions + dictLen int // Total number of entries in dictionary } -// instInfo is information gathered on an gcshape (or fully concrete) -// instantiation of a function. +// instInfo is information gathered on an shape instantiation of a function. type instInfo struct { fun *ir.Func // The instantiated function (with body) dictParam *ir.Name // The node inside fun that refers to the dictionary param - gf *ir.Name // The associated generic function - gfInfo *gfInfo - - startSubDict int // Start of dict entries for subdictionaries - startItabConv int // Start of dict entries for itab conversions - dictLen int // Total number of entries in dictionary - - // Map from nodes in instantiated fun (OCALL, OCALLMETHOD, OFUNCINST, and - // OMETHEXPR) to the associated dictionary entry for a sub-dictionary - dictEntryMap map[ir.Node]int + dictInfo *dictInfo } type irgen struct { @@ -155,13 +158,8 @@ type irgen struct { dnum int // for generating unique dictionary variables - // Map from generic function to information about its type params, derived - // types, and subdictionaries. - gfInfoMap map[*types.Sym]*gfInfo - // Map from a name of function that been instantiated to information about - // its instantiated function, associated generic function/method, and the - // mapping from IR nodes to dictionary entries. + // its instantiated function (including dictionary format). instInfoMap map[*types.Sym]*instInfo // dictionary syms which we need to finish, by writing out any itabconv @@ -179,10 +177,11 @@ func (g *irgen) later(fn func()) { } type delayInfo struct { - gf *ir.Name - targs []*types.Type - sym *types.Sym - off int + gf *ir.Name + targs []*types.Type + sym *types.Sym + off int + isMeth bool } type typeDelayInfo struct { diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 6c990c1828..e60383f4e0 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -44,7 +44,6 @@ func infoPrint(format string, a ...interface{}) { // process. func (g *irgen) stencil() { g.instInfoMap = make(map[*types.Sym]*instInfo) - g.gfInfoMap = make(map[*types.Sym]*gfInfo) // Instantiate the methods of instantiated generic types that we have seen so far. g.instantiateMethods() @@ -106,7 +105,7 @@ func (g *irgen) stencil() { inst := call.X.(*ir.InstExpr) nameNode, isMeth := g.getInstNameNode(inst) targs := typecheck.TypesOf(inst.Targs) - st := g.getInstantiation(nameNode, targs, isMeth) + st := g.getInstantiation(nameNode, targs, isMeth).fun dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth) if infoPrintMode { dictkind := "Main dictionary" @@ -165,7 +164,7 @@ func (g *irgen) stencil() { // to OCALLFUNC and does typecheckaste/assignconvfn. transformCall(call) - st := g.getInstantiation(gf, targs, true) + st := g.getInstantiation(gf, targs, true).fun dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true) // We have to be using a subdictionary, since this is // a generic method call. @@ -232,6 +231,31 @@ func (g *irgen) stencil() { } g.finalizeSyms() + + // All the instantiations and dictionaries have been created. Now go through + // each instantiation and transform the various operations that need to make + // use of their dictionary. + l := len(g.instInfoMap) + for _, info := range g.instInfoMap { + g.dictPass(info) + if doubleCheck { + ir.Visit(info.fun, func(n ir.Node) { + if n.Op() != ir.OCONVIFACE { + return + } + c := n.(*ir.ConvExpr) + if c.X.Type().HasShape() && !c.X.Type().IsInterface() { + ir.Dump("BAD FUNCTION", info.fun) + ir.Dump("BAD CONVERSION", c) + base.Fatalf("converting shape type to interface") + } + }) + } + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun) + } + } + assert(l == len(g.instInfoMap)) } // buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR @@ -274,7 +298,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { // For method values, the target expects a dictionary and the receiver // as its first two arguments. // dictValue is the value to use for the dictionary argument. - target = g.getInstantiation(gf, targs, rcvrValue != nil) + target = g.getInstantiation(gf, targs, rcvrValue != nil).fun dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil) if infoPrintMode { dictkind := "Main dictionary" @@ -321,7 +345,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node { // Remember if value method, so we can detect (*T).M case. valueMethod = true } - target = g.getInstantiation(gf, targs, true) + target = g.getInstantiation(gf, targs, true).fun dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true) if infoPrintMode { dictkind := "Main dictionary" @@ -544,13 +568,18 @@ func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Nam var dict ir.Node usingSubdict := false if declInfo != nil { - // Get the dictionary arg via sub-dictionary reference - entry, ok := declInfo.dictEntryMap[n] + entry := -1 + for i, de := range declInfo.dictInfo.subDictCalls { + if n == de { + entry = declInfo.dictInfo.startSubDict + i + break + } + } // If the entry is not found, it may be that this node did not have // any type args that depend on type params, so we need a main // dictionary, not a sub-dictionary. - if ok { - dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen) + if entry >= 0 { + dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen) usingSubdict = true } } @@ -577,7 +606,7 @@ func checkFetchBody(nameNode *ir.Name) { // getInstantiation gets the instantiantion and dictionary of the function or method nameNode // with the type arguments shapes. If the instantiated function is not already // cached, then it calls genericSubst to create the new instantiation. -func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *ir.Func { +func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo { checkFetchBody(nameNode) // Convert any non-shape type arguments to their shape, so we can reduce the @@ -586,12 +615,12 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth // specified concrete type args. var s1 []*types.Type for i, t := range shapes { - if !t.HasShape() { + if !t.IsShape() { if s1 == nil { s1 = make([]*types.Type, len(shapes)) copy(s1[0:i], shapes[0:i]) } - s1[i] = typecheck.Shapify(t) + s1[i] = typecheck.Shapify(t, i) } else if s1 != nil { s1[i] = shapes[i] } @@ -605,28 +634,28 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth if info == nil { // If instantiation doesn't exist yet, create it and add // to the list of decls. - gfInfo := g.getGfInfo(nameNode) info = &instInfo{ - gf: nameNode, - gfInfo: gfInfo, - startSubDict: len(shapes) + len(gfInfo.derivedTypes), - startItabConv: len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls), - dictLen: len(shapes) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls) + len(gfInfo.itabConvs), - dictEntryMap: make(map[ir.Node]int), - } - // genericSubst fills in info.dictParam and info.dictEntryMap. + dictInfo: &dictInfo{}, + } + info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type) + + // genericSubst fills in info.dictParam and info.tparamToBound. st := g.genericSubst(sym, nameNode, shapes, isMeth, info) info.fun = st g.instInfoMap[sym] = info + + // getInstInfo fills in info.dictInfo. + g.getInstInfo(st, shapes, info) + if base.Flag.W > 1 { + ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) + } + // This ensures that the linker drops duplicates of this instantiation. // All just works! st.SetDupok(true) g.target.Decls = append(g.target.Decls, st) - if base.Flag.W > 1 { - ir.Dump(fmt.Sprintf("\nstenciled %v", st), st) - } } - return info.fun + return info } // Struct containing info needed for doing the substitution as we create the @@ -647,7 +676,7 @@ type subster struct { // args shapes. For a method with a generic receiver, it returns an instantiated // function type where the receiver becomes the first parameter. For either a generic // method or function, a dictionary parameter is the added as the very first -// parameter. genericSubst fills in info.dictParam and info.dictEntryMap. +// parameter. genericSubst fills in info.dictParam and info.tparamToBound. func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func { var tparams []*types.Type if isMethod { @@ -744,23 +773,13 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*typ base.Fatalf("defnMap is not empty") } - ir.CurFunc = savef - - if doubleCheck { - ir.Visit(newf, func(n ir.Node) { - if n.Op() != ir.OCONVIFACE { - return - } - c := n.(*ir.ConvExpr) - if c.X.Type().HasShape() && !c.X.Type().IsInterface() { - ir.Dump("BAD FUNCTION", newf) - ir.Dump("BAD CONVERSION", c) - base.Fatalf("converting shape type to interface") - } - }) + for i, tp := range tparams { + info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound()) } - return newf + ir.CurFunc = savef + + return subst.newf } // localvar creates a new name node for the specified local variable and enters it @@ -870,11 +889,11 @@ func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node { // refers to a type param or a derived type that uses type params). It uses the // specified dictionary dictParam, rather than the one in info.dictParam. func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node { - if i < 0 || i >= info.startSubDict { + if i < 0 || i >= info.dictInfo.startSubDict { base.Fatalf(fmt.Sprintf("bad dict index %d", i)) } - r := getDictionaryEntry(pos, info.dictParam, i, info.startSubDict) + r := getDictionaryEntry(pos, info.dictParam, i, info.dictInfo.startSubDict) // change type of retrieved dictionary entry to *byte, which is the // standard typing of a *runtime._type in the compiler typed(types.Types[types.TUINT8].PtrTo(), r) @@ -936,17 +955,6 @@ func (subst *subster) node(n ir.Node) ir.Node { } } - for i, de := range subst.info.gfInfo.subDictCalls { - if de == x { - // Remember the dictionary entry associated with this - // node in the instantiated function - // TODO: make sure this remains correct with respect to the - // transformations below. - subst.info.dictEntryMap[m] = subst.info.startSubDict + i - break - } - } - ir.EditChildren(m, edit) m.SetTypecheck(1) @@ -1019,36 +1027,9 @@ func (subst *subster) node(n ir.Node) ir.Node { // we find in the OCALL case below that the method value // is actually called. mse := m.(*ir.SelectorExpr) - if src := mse.X.Type(); src.IsShape() { - // The only dot on a shape type value are methods. - if mse.X.Op() == ir.OTYPE { - // Method expression T.M - m = subst.g.buildClosure2(subst, m, x) - // No need for transformDot - buildClosure2 has already - // transformed to OCALLINTER/ODOTINTER. - } else { - // Implement x.M as a conversion-to-bound-interface - // 1) convert x to the bound interface - // 2) call M on that interface - gsrc := x.(*ir.SelectorExpr).X.Type() - bound := gsrc.Bound() - dst := bound - if dst.HasTParam() { - dst = subst.ts.Typ(dst) - } - if src.IsInterface() { - // If type arg is an interface (unusual case), - // we do a type assert to the type bound. - mse.X = assertToBound(subst.info, subst.info.dictParam, m.Pos(), mse.X, bound, dst) - } else { - mse.X = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), mse.X, x, dst, gsrc) - } - transformDot(mse, false) - } - } else { + if src := mse.X.Type(); !src.IsShape() { transformDot(mse, false) } - m.SetTypecheck(1) case ir.OCALL: call := m.(*ir.CallExpr) @@ -1103,7 +1084,7 @@ func (subst *subster) node(n ir.Node) ir.Node { // or channel receive to compute function value. transformCall(call) - case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.ODYNAMICDOTTYPE: + case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER: transformCall(call) case ir.OFUNCINST: @@ -1111,6 +1092,7 @@ func (subst *subster) node(n ir.Node) ir.Node { // in stencil() once we have created & attached the // instantiation to be called. + case ir.OXDOT, ir.ODOTTYPE, ir.ODOTTYPE2: default: base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op())) } @@ -1142,7 +1124,7 @@ func (subst *subster) node(n ir.Node) ir.Node { // Copy that closure variable to a local one. // Note: this allows the dictionary to be captured by child closures. // See issue 47723. - ldict := ir.NewNameAt(x.Pos(), subst.info.gf.Sym().Pkg.Lookup(".dict")) + ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(".dict")) typed(types.Types[types.TUINTPTR], ldict) ldict.Class = ir.PAUTO ldict.Curfn = newfn @@ -1154,16 +1136,11 @@ func (subst *subster) node(n ir.Node) ir.Node { // Create inst info for the instantiated closure. The dict // param is the closure variable for the dictionary of the // outer function. Since the dictionary is shared, use the - // same entries for startSubDict, dictLen, dictEntryMap. + // same dictInfo. cinfo := &instInfo{ - fun: newfn, - dictParam: ldict, - gf: subst.info.gf, - gfInfo: subst.info.gfInfo, - startSubDict: subst.info.startSubDict, - startItabConv: subst.info.startItabConv, - dictLen: subst.info.dictLen, - dictEntryMap: subst.info.dictEntryMap, + fun: newfn, + dictParam: ldict, + dictInfo: subst.info.dictInfo, } subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo @@ -1182,117 +1159,176 @@ func (subst *subster) node(n ir.Node) ir.Node { m = ir.UseClosure(newfn.OClosure, subst.g.target) m.(*ir.ClosureExpr).SetInit(subst.list(x.Init())) + } + return m + } + + return edit(n) +} + +// dictPass takes a function instantiation and does the transformations on the +// operations that need to make use of the dictionary param. +func (g *irgen) dictPass(info *instInfo) { + savef := ir.CurFunc + ir.CurFunc = info.fun + + var edit func(ir.Node) ir.Node + edit = func(m ir.Node) ir.Node { + ir.EditChildren(m, edit) + + switch m.Op() { + case ir.OCLOSURE: + newf := m.(*ir.ClosureExpr).Func + ir.CurFunc = newf + outerinfo := info + info = g.instInfoMap[newf.Nname.Sym()] + + body := newf.Body + for i, n := range body { + body[i] = edit(n) + } + + info = outerinfo + ir.CurFunc = info.fun + + case ir.OXDOT: + mse := m.(*ir.SelectorExpr) + src := mse.X.Type() + assert(src.IsShape()) + + // The only dot on a shape type value are methods. + if mse.X.Op() == ir.OTYPE { + // Method expression T.M + m = g.buildClosure2(info, m) + // No need for transformDot - buildClosure2 has already + // transformed to OCALLINTER/ODOTINTER. + } else { + // Implement x.M as a conversion-to-bound-interface + // 1) convert x to the bound interface + // 2) call M on that interface + dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()] + if src.IsInterface() { + // If type arg is an interface (unusual case), + // we do a type assert to the type bound. + mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst) + } else { + mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst) + } + transformDot(mse, false) + } + case ir.OCALL: + op := m.(*ir.CallExpr).X.Op() + if op != ir.OFUNCINST { + assert(op == ir.OMETHVALUE || op == ir.OCLOSURE || op == ir.ODYNAMICDOTTYPE || op == ir.ODYNAMICDOTTYPE2) + transformCall(m.(*ir.CallExpr)) + } + case ir.OCONVIFACE: - x := x.(*ir.ConvExpr) if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() { // Was T->interface{}, after stenciling it is now interface{}->interface{}. // No longer need the conversion. See issue 48276. m.(*ir.ConvExpr).SetOp(ir.OCONVNOP) break } + mce := m.(*ir.ConvExpr) // Note: x's argument is still typed as a type parameter. // m's argument now has an instantiated type. - if x.X.Type().HasTParam() || (x.X.Type().IsInterface() && x.Type().HasTParam()) { - m = convertUsingDictionary(subst.info, subst.info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type()) + if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) { + m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type()) } case ir.ODOTTYPE, ir.ODOTTYPE2: - if !x.Type().HasTParam() { + if !m.Type().HasShape() { break } dt := m.(*ir.TypeAssertExpr) var rt ir.Node if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() { - ix := findDictType(subst.info, x.Type()) + ix := findDictType(info, m.Type()) assert(ix >= 0) - rt = getDictionaryType(subst.info, subst.info.dictParam, dt.Pos(), ix) + rt = getDictionaryType(info, info.dictParam, dt.Pos(), ix) } else { // nonempty interface to noninterface. Need an itab. ix := -1 - for i, ic := range subst.info.gfInfo.itabConvs { - if ic == x { - ix = subst.info.startItabConv + i + for i, ic := range info.dictInfo.itabConvs { + if ic == m { + ix = info.dictInfo.startItabConv + i break } } assert(ix >= 0) - rt = getDictionaryEntry(dt.Pos(), subst.info.dictParam, ix, subst.info.dictLen) + rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen) } op := ir.ODYNAMICDOTTYPE - if x.Op() == ir.ODOTTYPE2 { + if m.Op() == ir.ODOTTYPE2 { op = ir.ODYNAMICDOTTYPE2 } m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt) m.SetType(dt.Type()) m.SetTypecheck(1) case ir.OCASE: - if _, ok := x.(*ir.CommClause); ok { + if _, ok := m.(*ir.CommClause); ok { // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE? break } - x := x.(*ir.CaseClause) m := m.(*ir.CaseClause) - for i, c := range x.List { - if c.Op() == ir.OTYPE && c.Type().HasTParam() { + for i, c := range m.List { + if c.Op() == ir.OTYPE && c.Type().HasShape() { // Use a *runtime._type for the dynamic type. - ix := findDictType(subst.info, c.Type()) + ix := findDictType(info, m.List[i].Type()) assert(ix >= 0) - dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen)) + dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)) // For type switch from nonempty interfaces to non-interfaces, we need an itab as well. if !m.List[i].Type().IsInterface() { - if _, ok := subst.info.gfInfo.type2switchType[c]; ok { + if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok { // Type switch from nonempty interface. We need a *runtime.itab // for the dynamic type. ix := -1 - for i, ic := range subst.info.gfInfo.itabConvs { - if ic == c { - ix = subst.info.startItabConv + i + for i, ic := range info.dictInfo.itabConvs { + if ic == m.List[i] { + ix = info.dictInfo.startItabConv + i break } } assert(ix >= 0) - dt.ITab = getDictionaryEntry(c.Pos(), subst.info.dictParam, ix, subst.info.dictLen) + dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen) } } typed(m.List[i].Type(), dt) m.List[i] = dt } } + } return m } - - return edit(n) + edit(info.fun) + ir.CurFunc = savef } // findDictType looks for type t in the typeparams or derived types in the generic // function info.gfInfo. This will indicate the dictionary entry with the // correct concrete type for the associated instantiated function. func findDictType(info *instInfo, t *types.Type) int { - return info.gfInfo.findDictType(t) -} - -func (gfInfo *gfInfo) findDictType(t *types.Type) int { - for i, dt := range gfInfo.tparams { + for i, dt := range info.dictInfo.shapeParams { if dt == t { return i } } - for i, dt := range gfInfo.derivedTypes { - if types.Identical(dt, t) { - return i + len(gfInfo.tparams) + for i, dt := range info.dictInfo.derivedTypes { + if types.IdenticalStrict(dt, t) { + return i + len(info.dictInfo.shapeParams) } } return -1 } -// convertUsingDictionary converts value v from instantiated type src to an interface -// type dst, by returning a new set of nodes that make use of a dictionary entry. src -// is the generic (not shape) type, and gn is the original generic node of the -// CONVIFACE node or XDOT node (for a bound method call) that is causing the +// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface +// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the +// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the // conversion. -func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, gn ir.Node, dst, src *types.Type) ir.Node { - assert(src.HasTParam() || src.IsInterface() && gn.Type().HasTParam()) +func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node { + assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape()) assert(dst.IsInterface()) if v.Type().IsInterface() { @@ -1305,8 +1341,7 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v v.SetTypecheck(1) return v } - gdst := gn.Type() // pre-stenciled destination type - if !gdst.HasTParam() { + if !in.Type().HasShape() { // Regular OCONVIFACE works if the destination isn't parameterized. v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v) v.SetTypecheck(1) @@ -1328,7 +1363,7 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v types.CalcSize(fn.Type()) call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil) typed(types.Types[types.TUINT8].PtrTo(), call) - ix := findDictType(info, gdst) + ix := findDictType(info, in.Type()) assert(ix >= 0) inter := getDictionaryType(info, dictParam, pos, ix) call.Args = []ir.Node{inter, itab} @@ -1344,16 +1379,16 @@ func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v // will be more efficient than converting to an empty interface first // and then type asserting to dst. ix := -1 - for i, ic := range info.gfInfo.itabConvs { - if ic == gn { - ix = info.startItabConv + i + for i, ic := range info.dictInfo.itabConvs { + if ic == in { + ix = info.dictInfo.startItabConv + i break } } assert(ix >= 0) - rt = getDictionaryEntry(pos, dictParam, ix, info.dictLen) + rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen) } else { - ix := findDictType(info, src) + ix := findDictType(info, v.Type()) assert(ix >= 0) // Load the actual runtime._type of the type parameter from the dictionary. rt = getDictionaryType(info, dictParam, pos, ix) @@ -1469,8 +1504,6 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) return sym } - info := g.getGfInfo(gf) - infoPrint("=== Creating dictionary %v\n", sym.Name) off := 0 // Emit an entry for each targ (concrete type or gcshape). @@ -1480,8 +1513,12 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) off = objw.SymPtr(lsym, off, s, 0) markTypeUsed(t, lsym) } + + instInfo := g.getInstantiation(gf, targs, isMeth) + info := instInfo.dictInfo + subst := typecheck.Tsubster{ - Tparams: info.tparams, + Tparams: info.shapeParams, Targs: targs, } // Emit an entry for each derived type (after substituting targs) @@ -1496,18 +1533,33 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) for _, n := range info.subDictCalls { var sym *types.Sym switch n.Op() { - case ir.OCALL: + case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH: call := n.(*ir.CallExpr) - if call.X.Op() == ir.OXDOT { + if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH { var nameNode *ir.Name se := call.X.(*ir.SelectorExpr) - if types.IsInterfaceMethod(se.Selection.Type) { + if se.X.Type().IsShape() { // This is a method call enabled by a type bound. + + // We need this extra check for type expressions, which + // don't add in the implicit XDOTs. tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel) tmpse = typecheck.AddImplicitDots(tmpse) tparam := tmpse.X.Type() - assert(tparam.IsTypeParam()) - recvType := targs[tparam.Index()] + if !tparam.IsShape() { + // The method expression is not + // really on a typeparam. + break + } + ix := -1 + for i, shape := range info.shapeParams { + if shape == tparam { + ix = i + break + } + } + assert(ix >= 0) + recvType := targs[ix] if recvType.IsInterface() || len(recvType.RParams()) == 0 { // No sub-dictionary entry is // actually needed, since the @@ -1526,8 +1578,10 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) } else { // This is the case of a normal // method call on a generic type. - nameNode = call.X.(*ir.SelectorExpr).Selection.Nname.(*ir.Name) - subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams() + recvType := deref(call.X.(*ir.SelectorExpr).X.Type()) + genRecvType := recvType.OrigSym().Def.Type() + nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) + subtargs := recvType.RParams() s2targs := make([]*types.Type, len(subtargs)) for i, t := range subtargs { s2targs[i] = subst.Typ(t) @@ -1560,14 +1614,16 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) } sym = g.getDictionarySym(nameNode, subtargs, false) - case ir.OXDOT: + case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE: selExpr := n.(*ir.SelectorExpr) - subtargs := deref(selExpr.X.Type()).RParams() + recvType := deref(selExpr.Selection.Type.Recv().Type) + genRecvType := recvType.OrigSym().Def.Type() + subtargs := recvType.RParams() s2targs := make([]*types.Type, len(subtargs)) for i, t := range subtargs { s2targs[i] = subst.Typ(t) } - nameNode := selExpr.Selection.Nname.(*ir.Name) + nameNode := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name) sym = g.getDictionarySym(nameNode, s2targs, true) default: @@ -1584,11 +1640,13 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) } } + g.instantiateMethods() delay := &delayInfo{ - gf: gf, - targs: targs, - sym: sym, - off: off, + gf: gf, + targs: targs, + sym: sym, + off: off, + isMeth: isMeth, } g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay) return sym @@ -1604,10 +1662,11 @@ func (g *irgen) finalizeSyms() { infoPrint("=== Finalizing dictionary %s\n", d.sym.Name) lsym := d.sym.Linksym() - info := g.getGfInfo(d.gf) + instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth) + info := instInfo.dictInfo subst := typecheck.Tsubster{ - Tparams: info.tparams, + Tparams: info.shapeParams, Targs: d.targs, } @@ -1615,10 +1674,10 @@ func (g *irgen) finalizeSyms() { for _, n := range info.itabConvs { var srctype, dsttype *types.Type switch n.Op() { - case ir.OXDOT: + case ir.OXDOT, ir.OMETHVALUE: se := n.(*ir.SelectorExpr) srctype = subst.Typ(se.X.Type()) - dsttype = subst.Typ(se.X.Type().Bound()) + dsttype = subst.Typ(info.shapeToBound[se.X.Type()]) found := false for i, m := range dsttype.AllMethods().Slice() { if se.Sel == m.Sym { @@ -1649,6 +1708,9 @@ func (g *irgen) finalizeSyms() { d.off = objw.Uintptr(lsym, d.off, 0) infoPrint(" + Unused itab entry for %v\n", srctype) } else { + // Make sure all new fully-instantiated types have + // their methods created before generating any itabs. + g.instantiateMethods() itabLsym := reflectdata.ITabLsym(srctype, dsttype) d.off = objw.SymPtr(lsym, d.off, itabLsym, 0) infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype) @@ -1687,66 +1749,50 @@ func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool return np } -// hasTParamNodes returns true if the type of any node in targs has a typeparam. -func hasTParamNodes(targs []ir.Node) bool { +// hasShapeNodes returns true if the type of any node in targs has a shape. +func hasShapeNodes(targs []ir.Node) bool { for _, n := range targs { - if n.Type().HasTParam() { + if n.Type().HasShape() { return true } } return false } -// hasTParamNodes returns true if any type in targs has a typeparam. -func hasTParamTypes(targs []*types.Type) bool { +// hasShapeTypes returns true if any type in targs has a shape. +func hasShapeTypes(targs []*types.Type) bool { for _, t := range targs { - if t.HasTParam() { + if t.HasShape() { return true } } return false } -// getGfInfo get information for a generic function - type params, derived generic -// types, and subdictionaries. -func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { - infop := g.gfInfoMap[gn.Sym()] - if infop != nil { - return infop - } +// getInstInfo get the dictionary format for a function instantiation- type params, derived +// types, and needed subdictionaries and itabs. +func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) { + info := instInfo.dictInfo + info.shapeParams = shapes - checkFetchBody(gn) - var info gfInfo - gf := gn.Func - recv := gf.Type().Recv() - if recv != nil { - info.tparams = deref(recv.Type).RParams() - } else { - tparams := gn.Type().TParams().FieldSlice() - info.tparams = make([]*types.Type, len(tparams)) - for i, f := range tparams { - info.tparams[i] = f.Type - } - } - - for _, t := range info.tparams { - b := t.Bound() - if b.HasTParam() { + for _, t := range info.shapeParams { + b := info.shapeToBound[t] + if b.HasShape() { // If a type bound is parameterized (unusual case), then we // may need its derived type to do a type assert when doing a // bound call for a type arg that is an interface. - addType(&info, nil, b) + addType(info, nil, b) } } - for _, n := range gf.Dcl { - addType(&info, n, n.Type()) - n.DictIndex = uint16(info.findDictType(n.Type()) + 1) + for _, n := range st.Dcl { + addType(info, n, n.Type()) + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) } if infoPrintMode { - fmt.Printf(">>> GfInfo for %v\n", gn) - for _, t := range info.tparams { + fmt.Printf(">>> InstInfo for %v\n", st) + for _, t := range info.shapeParams { fmt.Printf(" Typeparam %v\n", t) } } @@ -1754,14 +1800,14 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { var visitFunc func(ir.Node) visitFunc = func(n ir.Node) { if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() { - if hasTParamNodes(n.(*ir.InstExpr).Targs) { + if hasShapeNodes(n.(*ir.InstExpr).Targs) { infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X) info.subDictCalls = append(info.subDictCalls, n) } - } else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() && - n.(*ir.SelectorExpr).Selection != nil && + } else if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && !n.(*ir.SelectorExpr).Implicit() && + !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { - if hasTParamTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) { + if hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) { if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE { infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n) } else { @@ -1772,34 +1818,33 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST { n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true) - if hasTParamNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) { + if hasShapeNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) { infoPrint(" Subdictionary at generic function/method call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n) info.subDictCalls = append(info.subDictCalls, n) } } - if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT && - n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil && + if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && + //n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil && len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 { n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true) - if hasTParamTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) { + if hasShapeTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) { infoPrint(" Subdictionary at generic method call: %v\n", n) info.subDictCalls = append(info.subDictCalls, n) } } if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT && - n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil && - deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).IsTypeParam() { + isShapeDeref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()) { n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true) infoPrint(" Optional subdictionary at generic bound call: %v\n", n) info.subDictCalls = append(info.subDictCalls, n) } if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() && !n.Type().IsEmptyInterface() && - n.(*ir.ConvExpr).X.Type().HasTParam() { + n.(*ir.ConvExpr).X.Type().HasShape() { infoPrint(" Itab for interface conv: %v\n", n) info.itabConvs = append(info.itabConvs, n) } - if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsTypeParam() { + if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsShape() { infoPrint(" Itab for bound call: %v\n", n) info.itabConvs = append(info.itabConvs, n) } @@ -1816,13 +1861,13 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { ir.Visit(n1, visitFunc) } for _, n := range cfunc.Dcl { - n.DictIndex = uint16(info.findDictType(n.Type()) + 1) + n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1) } } if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() { for _, cc := range n.(*ir.SwitchStmt).Cases { for _, c := range cc.List { - if c.Op() == ir.OTYPE && c.Type().HasTParam() { + if c.Op() == ir.OTYPE && c.Type().HasShape() { // Type switch from a non-empty interface - might need an itab. infoPrint(" Itab for type switch: %v\n", c) info.itabConvs = append(info.itabConvs, c) @@ -1834,41 +1879,48 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo { } } } - addType(&info, n, n.Type()) + addType(info, n, n.Type()) } - for _, stmt := range gf.Body { + for _, stmt := range st.Body { ir.Visit(stmt, visitFunc) } if infoPrintMode { for _, t := range info.derivedTypes { fmt.Printf(" Derived type %v\n", t) } - fmt.Printf(">>> Done Gfinfo\n") + fmt.Printf(">>> Done Instinfo\n") } - g.gfInfoMap[gn.Sym()] = &info - return &info + info.startSubDict = len(info.shapeParams) + len(info.derivedTypes) + info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs) +} + +// isShapeDeref returns true if t is either a shape or a pointer to a shape. (We +// can't just use deref(t).IsShape(), since a shape type is a complex type and may +// have a pointer as part of its shape.) +func isShapeDeref(t *types.Type) bool { + return t.IsShape() || t.IsPtr() && t.Elem().IsShape() } // addType adds t to info.derivedTypes if it is parameterized type (which is not -// just a simple type param) that is different from any existing type on +// just a simple shape) that is different from any existing type on // info.derivedTypes. -func addType(info *gfInfo, n ir.Node, t *types.Type) { - if t == nil || !t.HasTParam() { +func addType(info *dictInfo, n ir.Node, t *types.Type) { + if t == nil || !t.HasShape() { return } - if t.IsTypeParam() && t.Underlying() == t { + if t.IsShape() { return } if t.Kind() == types.TFUNC && n != nil && - (t.Recv() != nil || - n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) { + (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) { // Don't use the type of a named generic function or method, // since that is parameterized by other typeparams. // (They all come from arguments of a FUNCINST node.) return } - if doubleCheck && !parameterizedBy(t, info.tparams) { + if doubleCheck && !parameterizedBy(t, info.shapeParams) { base.Fatalf("adding type with invalid parameters %+v", t) } if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() { @@ -1877,7 +1929,7 @@ func addType(info *gfInfo, n ir.Node, t *types.Type) { } // Ignore a derived type we've already added. for _, et := range info.derivedTypes { - if types.Identical(t, et) { + if types.IdenticalStrict(t, et) { return } } @@ -1903,8 +1955,7 @@ func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Ty } return true } - switch t.Kind() { - case types.TTYPEPARAM: + if t.IsShape() { // Check if t is one of the allowed parameters in scope. for _, p := range params { if p == t { @@ -1914,6 +1965,8 @@ func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Ty // Couldn't find t in the list of allowed parameters. return false + } + switch t.Kind() { case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN: return parameterizedBy1(t.Elem(), params, visited) @@ -2004,17 +2057,17 @@ func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*t } // assertToBound returns a new node that converts a node rcvr with interface type to -// the 'dst' interface type. bound is the unsubstituted form of dst. -func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, bound, dst *types.Type) ir.Node { - if bound.HasTParam() { - ix := findDictType(info, bound) +// the 'dst' interface type. +func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node { + if dst.HasShape() { + ix := findDictType(info, dst) assert(ix >= 0) rt := getDictionaryType(info, dictVar, pos, ix) rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt) typed(dst, rcvr) } else { rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil) - typed(bound, rcvr) + typed(dst, rcvr) } return rcvr } @@ -2026,9 +2079,8 @@ func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, // // The returned closure is fully substituted and has already had any needed // transformations done. -func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node { - outer := subst.newf - info := subst.info +func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node { + outer := info.fun pos := m.Pos() typ := m.Type() // type of the closure @@ -2051,24 +2103,18 @@ func (g *irgen) buildClosure2(subst *subster, m, x ir.Node) ir.Node { rcvr := args[0] args = args[1:] assert(m.(*ir.SelectorExpr).X.Type().IsShape()) - gsrc := x.(*ir.SelectorExpr).X.Type() - bound := gsrc.Bound() - dst := bound - if dst.HasTParam() { - dst = subst.ts.Typ(bound) - } + dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()] if m.(*ir.SelectorExpr).X.Type().IsInterface() { // If type arg is an interface (unusual case), we do a type assert to // the type bound. - rcvr = assertToBound(info, dictVar, pos, rcvr, bound, dst) + rcvr = assertToBound(info, dictVar, pos, rcvr, dst) } else { - rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, x, dst, gsrc) + rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst) } - dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, x.(*ir.SelectorExpr).Sel) + dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel) dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1) - // Do a type substitution on the generic bound, in case it is parameterized. - typed(subst.ts.Typ(x.(*ir.SelectorExpr).Selection.Type), dot) + typed(dot.Selection.Type, dot) innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args) t := m.Type() if t.NumResults() == 0 { diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 183ede789e..f42bb338d0 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -1921,7 +1921,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy // Target method uses shaped names. targs2 := make([]*types.Type, len(targs)) for i, t := range targs { - targs2[i] = typecheck.Shapify(t) + targs2[i] = typecheck.Shapify(t, i) } targs = targs2 diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 5854e3c458..64d30eeb5a 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -1019,10 +1019,11 @@ type Tsubster struct { SubstForwFunc func(*types.Type) *types.Type } -// Typ computes the type obtained by substituting any type parameter in t with the -// corresponding type argument in subst. If t contains no type parameters, the -// result is t; otherwise the result is a new type. It deals with recursive types -// by using TFORW types and finding partially or fully created types via sym.Def. +// Typ computes the type obtained by substituting any type parameter or shape in t +// that appears in subst.Tparams with the corresponding type argument in subst.Targs. +// If t contains no type parameters, the result is t; otherwise the result is a new +// type. It deals with recursive types by using TFORW types and finding partially or +// fully created types via sym.Def. func (ts *Tsubster) Typ(t *types.Type) *types.Type { // Defer the CheckSize calls until we have fully-defined // (possibly-recursive) top-level type. @@ -1033,14 +1034,14 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type { } func (ts *Tsubster) typ1(t *types.Type) *types.Type { - if !t.HasTParam() && t.Kind() != types.TFUNC { + if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC { // Note: function types need to be copied regardless, as the // types of closures may contain declarations that need // to be copied. See #45738. return t } - if t.IsTypeParam() { + if t.IsTypeParam() || t.IsShape() { for i, tp := range ts.Tparams { if tp == t { return ts.Targs[i] @@ -1072,14 +1073,14 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type { var targsChanged bool var forw *types.Type - if t.Sym() != nil && t.HasTParam() { + if t.Sym() != nil && (t.HasTParam() || t.HasShape()) { // Need to test for t.HasTParam() again because of special TFUNC case above. // Translate the type params for this type according to // the tparam/targs mapping from subst. neededTargs = make([]*types.Type, len(t.RParams())) for i, rparam := range t.RParams() { neededTargs[i] = ts.typ1(rparam) - if !types.Identical(neededTargs[i], rparam) { + if !types.IdenticalStrict(neededTargs[i], rparam) { targsChanged = true } } @@ -1286,7 +1287,7 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type { // fields, set force to true. func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type { if t.NumFields() == 0 { - if t.HasTParam() { + if t.HasTParam() || t.HasShape() { // For an empty struct, we need to return a new type, // since it may now be fully instantiated (HasTParam // becomes false). @@ -1388,19 +1389,20 @@ func genericTypeName(sym *types.Sym) string { return sym.Name[0:strings.Index(sym.Name, "[")] } -// Shapify takes a concrete type and returns a GCshape type that can +// Shapify takes a concrete type and a type param index, and returns a GCshape type that can // be used in place of the input type and still generate identical code. // No methods are added - all methods calls directly on a shape should // be done by converting to an interface using the dictionary. // -// TODO: this could take the generic function and base its decisions -// on how that generic function uses this type argument. For instance, -// if it doesn't use it as a function argument/return value, then -// we don't need to distinguish int64 and float64 (because they only -// differ in how they get passed as arguments). For now, we only -// unify two different types if they are identical in every possible way. -func Shapify(t *types.Type) *types.Type { - assert(!t.HasShape()) +// For now, we only consider two types to have the same shape, if they have exactly +// the same underlying type or they are both pointer types. +// +// Shape types are also distinguished by the index of the type in a type param/arg +// list. We need to do this so we can distinguish and substitute properly for two +// type params in the same function that have the same shape for a particular +// instantiation. +func Shapify(t *types.Type, index int) *types.Type { + assert(!t.IsShape()) // Map all types with the same underlying type to the same shape. u := t.Underlying() @@ -1410,15 +1412,24 @@ func Shapify(t *types.Type) *types.Type { u = types.Types[types.TUINT8].PtrTo() } - if s := shaped[u]; s != nil { + if shapeMap == nil { + shapeMap = map[int]map[*types.Type]*types.Type{} + } + submap := shapeMap[index] + if submap == nil { + submap = map[*types.Type]*types.Type{} + shapeMap[index] = submap + } + if s := submap[u]; s != nil { return s } - sym := types.ShapePkg.Lookup(u.LinkString()) + nm := fmt.Sprintf("%s_%d", u.LinkString(), index) + sym := types.ShapePkg.Lookup(nm) if sym.Def != nil { // Use any existing type with the same name - shaped[u] = sym.Def.Type() - return shaped[u] + submap[u] = sym.Def.Type() + return submap[u] } name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym) s := types.NewNamed(name) @@ -1428,8 +1439,8 @@ func Shapify(t *types.Type) *types.Type { s.SetHasShape(true) name.SetType(s) name.SetTypecheck(1) - shaped[u] = s + submap[u] = s return s } -var shaped = map[*types.Type]*types.Type{} +var shapeMap map[int]map[*types.Type]*types.Type diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go index 2e9e2f4fd8..dce7d29143 100644 --- a/src/cmd/compile/internal/types/identity.go +++ b/src/cmd/compile/internal/types/identity.go @@ -4,19 +4,30 @@ package types +const ( + identIgnoreTags = 1 << iota + identStrict +) + // Identical reports whether t1 and t2 are identical types, following the spec rules. // Receiver parameter types are ignored. Named (defined) types are only equal if they // are pointer-equal - i.e. there must be a unique types.Type for each specific named // type. Also, a type containing a shape type is considered identical to another type // (shape or not) if their underlying types are the same, or they are both pointers. func Identical(t1, t2 *Type) bool { - return identical(t1, t2, true, nil) + return identical(t1, t2, 0, nil) } // IdenticalIgnoreTags is like Identical, but it ignores struct tags // for struct identity. func IdenticalIgnoreTags(t1, t2 *Type) bool { - return identical(t1, t2, false, nil) + return identical(t1, t2, identIgnoreTags, nil) +} + +// IdenticalStrict is like Identical, but matches types exactly, without the +// exception for shapes. +func IdenticalStrict(t1, t2 *Type) bool { + return identical(t1, t2, identStrict, nil) } type typePair struct { @@ -24,7 +35,7 @@ type typePair struct { t2 *Type } -func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool { +func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool { if t1 == t2 { return true } @@ -32,7 +43,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b return false } if t1.sym != nil || t2.sym != nil { - if t1.HasShape() || t2.HasShape() { + if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) { switch t1.kind { case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR: return true @@ -78,7 +89,7 @@ cont: } for i, f1 := range t1.AllMethods().Slice() { f2 := t2.AllMethods().Index(i) - if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } } @@ -90,10 +101,10 @@ cont: } for i, f1 := range t1.FieldSlice() { f2 := t2.Field(i) - if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } - if cmpTags && f1.Note != f2.Note { + if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note { return false } } @@ -111,7 +122,7 @@ cont: } for i, f1 := range fs1 { f2 := fs2[i] - if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) { + if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) { return false } } @@ -129,10 +140,10 @@ cont: } case TMAP: - if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) { + if !identical(t1.Key(), t2.Key(), flags, assumedEqual) { return false } } - return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual) + return identical(t1.Elem(), t2.Elem(), flags, assumedEqual) } -- cgit v1.2.3-54-g00ecf From cfa233d76bcff00f46f5e5acdb17cb819a309d2b Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sun, 12 Sep 2021 12:21:48 -0700 Subject: cmd/compile: remove unneeded early transforms, with dictionary change Now that we are computing the dictionary format on the instantiated functions, we can remove the early transformation code that was needed to create the implicit CONVIFACE nodes in the generic function. Change-Id: I1695484e7d59bccbfb757994f3e40e84288759a5 Reviewed-on: https://go-review.googlesource.com/c/go/+/349614 Run-TryBot: Dan Scales TryBot-Result: Go Bot Trust: Dan Scales Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/expr.go | 38 ------------- src/cmd/compile/internal/noder/helpers.go | 11 ---- src/cmd/compile/internal/noder/stmt.go | 9 ---- src/cmd/compile/internal/noder/transform.go | 84 ++++++++--------------------- src/cmd/compile/internal/typecheck/subr.go | 3 ++ 5 files changed, 26 insertions(+), 119 deletions(-) diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index 045f028e1a..3e1960f7a4 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -250,44 +250,6 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto // only be fully transformed once it has an instantiated type. n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value)) typed(g.typ(typ), n) - - // Fill in n.Selection for a generic method reference or a bound - // interface method, even though we won't use it directly, since it - // is useful for analysis. Specifically do not fill in for fields or - // other interfaces methods (method call on an interface value), so - // n.Selection being non-nil means a method reference for a generic - // type or a method reference due to a bound. - obj2 := g.info.Selections[expr].Obj() - sig := types2.AsSignature(obj2.Type()) - if sig == nil || sig.Recv() == nil { - return n - } - index := g.info.Selections[expr].Index() - last := index[len(index)-1] - // recvType is the receiver of the method being called. Because of the - // way methods are imported, g.obj(obj2) doesn't work across - // packages, so we have to lookup the method via the receiver type. - recvType := deref2(sig.Recv().Type()) - if types2.AsInterface(recvType.Underlying()) != nil { - fieldType := n.X.Type() - for _, ix := range index[:len(index)-1] { - fieldType = deref(fieldType).Field(ix).Type - } - if fieldType.Kind() == types.TTYPEPARAM { - n.Selection = fieldType.Bound().AllMethods().Index(last) - //fmt.Printf(">>>>> %v: Bound call %v\n", base.FmtPos(pos), n.Sel) - } else { - assert(fieldType.Kind() == types.TINTER) - //fmt.Printf(">>>>> %v: Interface call %v\n", base.FmtPos(pos), n.Sel) - } - return n - } - - recvObj := types2.AsNamed(recvType).Obj() - recv := g.pkg(recvObj.Pkg()).Lookup(recvObj.Name()).Def - n.Selection = recv.Type().Methods().Index(last) - //fmt.Printf(">>>>> %v: Method call %v\n", base.FmtPos(pos), n.Sel) - return n } diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index 9487e76336..f06dd8b065 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -189,17 +189,6 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) // A function instantiation (even if fully concrete) shouldn't be // transformed yet, because we need to add the dictionary during the // transformation. - // - // However, if we have a function type (even though it is - // parameterized), then we can add in any needed CONVIFACE nodes via - // typecheckaste(). We need to call transformArgs() to deal first - // with the f(g(()) case where g returns multiple return values. We - // can't do anything if fun is a type param (which is probably - // described by a structural constraint) - if fun.Type().Kind() == types.TFUNC { - transformArgs(n) - typecheckaste(ir.OCALL, fun, n.IsDDD, fun.Type().Params(), n.Args, true) - } return typed(typ, n) } diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index 146761c23f..1c366296fc 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -101,8 +101,6 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { n.Def = initDefn(n, names) if delay { - earlyTransformAssign(n, lhs, rhs) - n.X, n.Y = lhs[0], rhs[0] n.SetTypecheck(3) return n } @@ -117,7 +115,6 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs) n.Def = initDefn(n, names) if delay { - earlyTransformAssign(n, lhs, rhs) n.SetTypecheck(3) return n } @@ -135,12 +132,6 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { if e.Type().HasTParam() { // Delay transforming the return statement if any of the // return values have a type param. - if !ir.HasNamedResults(ir.CurFunc) { - transformArgs(n) - // But add CONVIFACE nodes where needed if - // any of the return values have interface type. - typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), n.Results, true) - } n.SetTypecheck(3) return n } diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index 8173bfc747..91374054b6 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -157,7 +157,7 @@ func transformCall(n *ir.CallExpr) { n.SetOp(ir.OCALLFUNC) } - typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, false) + typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args) if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 { typecheck.FixMethodCall(n) } @@ -365,59 +365,6 @@ assignOK: } } -// Version of transformAssign that can run on generic code that adds CONVIFACE calls -// as needed (and rewrites multi-value calls). -func earlyTransformAssign(stmt ir.Node, lhs, rhs []ir.Node) { - cr := len(rhs) - if len(rhs) == 1 { - if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() { - cr = rtyp.NumFields() - } - } - - // x,y,z = f() - _, isCallExpr := rhs[0].(*ir.CallExpr) - if isCallExpr && cr > len(rhs) { - stmt := stmt.(*ir.AssignListStmt) - stmt.SetOp(ir.OAS2FUNC) - r := rhs[0].(*ir.CallExpr) - rtyp := r.Type() - - mismatched := false - failed := false - for i := range lhs { - result := rtyp.Field(i).Type - - if lhs[i].Type() == nil || result == nil { - failed = true - } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { - mismatched = true - } - } - if mismatched && !failed { - typecheck.RewriteMultiValueCall(stmt, r) - } - return - } - - // x, ok = y - if len(lhs) != len(rhs) { - assert(len(lhs) == 2 && len(rhs) == 1) - // TODO(danscales): deal with case where x or ok is an interface - // type. We want to add CONVIFACE now, but that is tricky, because - // the rhs may be AS2MAPR, AS2RECV, etc. which has two result values, - // and that is not rewritten until the order phase (o.stmt, as2ok). - return - } - - // Check for interface conversion on each assignment - for i, r := range rhs { - if lhs[i].Type() != nil && lhs[i].Type().IsInterface() { - rhs[i] = assignconvfn(r, lhs[i].Type()) - } - } -} - // Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls. func transformArgs(n ir.InitNode) { var list []ir.Node @@ -457,11 +404,11 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node { return n } - if types.Identical(n.Type(), t) { + if types.IdenticalStrict(n.Type(), t) { return n } - op, why := typecheck.Assignop(n.Type(), t) + op, why := Assignop(n.Type(), t) if op == ir.OXXX { base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why) } @@ -472,11 +419,26 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node { return r } +func Assignop(src, dst *types.Type) (ir.Op, string) { + if src == dst { + return ir.OCONVNOP, "" + } + if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil { + return ir.OXXX, "" + } + + // 1. src type is identical to dst. + if types.IdenticalStrict(src, dst) { + return ir.OCONVNOP, "" + } + return typecheck.Assignop1(src, dst) +} + // Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly // only. If convifaceOnly is true, we only do interface conversion. We use this to do // early insertion of CONVIFACE nodes during noder2, when the function or args may // have typeparams. -func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes, convifaceOnly bool) { +func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) { var t *types.Type var i int @@ -495,7 +457,7 @@ func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl i if isddd { n = nl[i] ir.SetPos(n) - if n.Type() != nil && (!convifaceOnly || t.IsInterface()) { + if n.Type() != nil { nl[i] = assignconvfn(n, t) } return @@ -505,7 +467,7 @@ func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl i for ; i < len(nl); i++ { n = nl[i] ir.SetPos(n) - if n.Type() != nil && (!convifaceOnly || t.IsInterface()) { + if n.Type() != nil { nl[i] = assignconvfn(n, t.Elem()) } } @@ -514,7 +476,7 @@ func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl i n = nl[i] ir.SetPos(n) - if n.Type() != nil && (!convifaceOnly || t.IsInterface()) { + if n.Type() != nil { nl[i] = assignconvfn(n, t) } i++ @@ -536,7 +498,7 @@ func transformReturn(rs *ir.ReturnStmt) { return } - typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl, false) + typecheckaste(ir.ORETURN, nil, false, ir.CurFunc.Type().Results(), nl) } // transformSelect transforms a select node, creating an assignment list as needed diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 64d30eeb5a..d4af4e172e 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -352,7 +352,10 @@ func Assignop(src, dst *types.Type) (ir.Op, string) { if types.Identical(src, dst) { return ir.OCONVNOP, "" } + return Assignop1(src, dst) +} +func Assignop1(src, dst *types.Type) (ir.Op, string) { // 2. src and dst have identical underlying types and // a. either src or dst is not a named type, or // b. both are empty interface types, or -- cgit v1.2.3-54-g00ecf From e7dbe3908e17ec77851161e6cb98c7161823aa0d Mon Sep 17 00:00:00 2001 From: Leonard Wang Date: Sat, 24 Jul 2021 22:17:21 +0800 Subject: cmd/cgo: add missing tab in exports for a result of void Change-Id: I45575afbad364c13b7179ebe3f3dfc4ed9671d2b Reviewed-on: https://go-review.googlesource.com/c/go/+/336891 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Reviewed-by: Meng Zhuo --- src/cmd/cgo/out.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index ee989b95e5..93cc0c6dc9 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -1054,9 +1054,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgo2, "\t") + if gccResult != "void" { // Write results back to frame. - fmt.Fprintf(fgo2, "\t") forFieldList(fntype.Results, func(i int, aname string, atype ast.Expr) { if i > 0 { -- cgit v1.2.3-54-g00ecf From 04f5116c987082a834a29e81c81ece8537d87219 Mon Sep 17 00:00:00 2001 From: gerrard Date: Thu, 16 Sep 2021 07:10:58 +0000 Subject: cmd/go: clean paths before checking same directory Replace `filepath.Split` with `filepath.Dir`. Clean paths before checking whether command line files are in same directory. Fixes #47392 Change-Id: I259c3024e7670e78833622b02af4710bc4b68b31 GitHub-Last-Rev: c7c4905bb9c62737e95a4663813f076ee540046b GitHub-Pull-Request: golang/go#47412 Reviewed-on: https://go-review.googlesource.com/c/go/+/337629 Trust: Bryan C. Mills Trust: Cherry Mui Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot Reviewed-by: Jay Conrod --- src/cmd/go/internal/load/pkg.go | 5 +---- src/cmd/go/testdata/script/run_dirs.txt | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index f0613a4c0a..4013330bc4 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -2674,10 +2674,7 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa if fi.IsDir() { base.Fatalf("%s is a directory, should be a Go file", file) } - dir1, _ := filepath.Split(file) - if dir1 == "" { - dir1 = "./" - } + dir1 := filepath.Dir(file) if dir == "" { dir = dir1 } else if dir != dir1 { diff --git a/src/cmd/go/testdata/script/run_dirs.txt b/src/cmd/go/testdata/script/run_dirs.txt index 538a6ac6f3..bd5cfbe3fb 100644 --- a/src/cmd/go/testdata/script/run_dirs.txt +++ b/src/cmd/go/testdata/script/run_dirs.txt @@ -1,11 +1,21 @@ cd rundir ! go run x.go sub/sub.go -stderr 'named files must all be in one directory; have ./ and sub/' +stderr 'named files must all be in one directory; have . and sub' ! go run sub/sub.go x.go -stderr 'named files must all be in one directory; have sub/ and ./' +stderr 'named files must all be in one directory; have sub and .' + +cd ../ +go run rundir/foo.go ./rundir/bar.go +stderr 'hello world' -- rundir/sub/sub.go -- package main -- rundir/x.go -- package main +-- rundir/foo.go -- +package main +func main() { println(msg) } +-- rundir/bar.go -- +package main +const msg = "hello world" -- cgit v1.2.3-54-g00ecf From b1bedc0774d8a3a7ff8778e933ee92e8638e9493 Mon Sep 17 00:00:00 2001 From: nimelehin Date: Wed, 15 Sep 2021 04:30:49 +0000 Subject: cmd/go: add GOAMD64 environment variable The variable represents the microarchitecture level for which to compile. Valid values are v1 (default), v2, v3, v4. Updates #45453 Change-Id: I095197fc9239d79f98896d7e745e2341354daca4 GitHub-Last-Rev: f83ed17204606264073be5b9831f9d24f2f9dbc4 GitHub-Pull-Request: golang/go#48359 Reviewed-on: https://go-review.googlesource.com/c/go/+/349595 Reviewed-by: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Bryan C. Mills --- src/cmd/dist/build.go | 11 +++++++++++ src/cmd/dist/buildruntime.go | 1 + src/cmd/go/alldocs.go | 4 ++++ src/cmd/go/internal/cfg/cfg.go | 3 +++ src/cmd/go/internal/help/helpdoc.go | 4 ++++ src/cmd/go/internal/work/gc.go | 5 +++++ src/internal/buildcfg/cfg.go | 16 ++++++++++++++++ src/internal/buildcfg/cfg_test.go | 25 +++++++++++++++++++++++++ src/internal/cfg/cfg.go | 1 + test/run.go | 2 +- 10 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/internal/buildcfg/cfg_test.go diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 33a329e48b..39f016e315 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -32,6 +32,7 @@ var ( goos string goarm string go386 string + goamd64 string gomips string gomips64 string goppc64 string @@ -145,6 +146,12 @@ func xinit() { } go386 = b + b = os.Getenv("GOAMD64") + if b == "" { + b = "v1" + } + goamd64 = b + b = os.Getenv("GOMIPS") if b == "" { b = "hardfloat" @@ -217,6 +224,7 @@ func xinit() { // For tools being invoked but also for os.ExpandEnv. os.Setenv("GO386", go386) + os.Setenv("GOAMD64", goamd64) os.Setenv("GOARCH", goarch) os.Setenv("GOARM", goarm) os.Setenv("GOHOSTARCH", gohostarch) @@ -1181,6 +1189,9 @@ func cmdenv() { if goarch == "386" { xprintf(format, "GO386", go386) } + if goarch == "amd64" { + xprintf(format, "GOAMD64", goamd64) + } if goarch == "mips" || goarch == "mipsle" { xprintf(format, "GOMIPS", gomips) } diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index 54e935ad3b..fdc1d25774 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -60,6 +60,7 @@ func mkbuildcfg(file string) { fmt.Fprintf(&buf, "import \"runtime\"\n") fmt.Fprintln(&buf) fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386) + fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64) fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm) fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips) fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index b7e8212795..35c60744b8 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1987,6 +1987,10 @@ // GO386 // For GOARCH=386, how to implement floating point instructions. // Valid values are sse2 (default), softfloat. +// GOAMD64 +// For GOARCH=GOAMD64, the microarchitecture level for which to compile. +// Valid values are v1 (default), v2, v3, v4. +// See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels. // GOMIPS // For GOARCH=mips{,le}, whether to use floating point instructions. // Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index da616ee1dd..5f4465e06b 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -263,6 +263,7 @@ var ( // Used in envcmd.MkEnv and build ID computations. GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM)) GO386 = envOr("GO386", buildcfg.GO386) + GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64)) GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS) GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64)) @@ -289,6 +290,8 @@ func GetArchEnv() (key, val string) { return "GOARM", GOARM case "386": return "GO386", GO386 + case "amd64": + return "GOAMD64", GOAMD64 case "mips", "mipsle": return "GOMIPS", GOMIPS case "mips64", "mips64le": diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 490ff1fb7c..91876cefe0 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -592,6 +592,10 @@ Architecture-specific environment variables: GO386 For GOARCH=386, how to implement floating point instructions. Valid values are sse2 (default), softfloat. + GOAMD64 + For GOARCH=GOAMD64, the microarchitecture level for which to compile. + Valid values are v1 (default), v2, v3, v4. + See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels. GOMIPS For GOARCH=mips{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go index 1cce5d4dd5..800800f788 100644 --- a/src/cmd/go/internal/work/gc.go +++ b/src/cmd/go/internal/work/gc.go @@ -379,6 +379,11 @@ func asmArgs(a *Action, p *load.Package) []interface{} { args = append(args, "-D", "GO386_"+cfg.GO386) } + if cfg.Goarch == "amd64" { + // Define GOAMD64_value from cfg.GOAMD64. + args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64) + } + if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" { // Define GOMIPS_value from cfg.GOMIPS. args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS) diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go index 9fe7f211fb..68c10a2824 100644 --- a/src/internal/buildcfg/cfg.go +++ b/src/internal/buildcfg/cfg.go @@ -25,6 +25,7 @@ var ( GOARCH = envOr("GOARCH", defaultGOARCH) GOOS = envOr("GOOS", defaultGOOS) GO386 = envOr("GO386", defaultGO386) + GOAMD64 = goamd64() GOARM = goarm() GOMIPS = gomips() GOMIPS64 = gomips64() @@ -52,6 +53,21 @@ func envOr(key, value string) string { return value } +func goamd64() int { + switch v := envOr("GOAMD64", defaultGOAMD64); v { + case "v1": + return 1 + case "v2": + return 2 + case "v3": + return 3 + case "v4": + return 4 + } + Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4") + return int(defaultGOAMD64[len("v")] - '0') +} + func goarm() int { def := defaultGOARM if GOOS == "android" && GOARCH == "arm" { diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go new file mode 100644 index 0000000000..9180441c28 --- /dev/null +++ b/src/internal/buildcfg/cfg_test.go @@ -0,0 +1,25 @@ +// Copyright 2021 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 buildcfg + +import ( + "os" + "testing" +) + +func TestConfigFlags(t *testing.T) { + os.Setenv("GOAMD64", "v1") + if goamd64() != 1 { + t.Errorf("Wrong parsing of GOAMD64=v1") + } + os.Setenv("GOAMD64", "v4") + if goamd64() != 4 { + t.Errorf("Wrong parsing of GOAMD64=v4") + } + os.Setenv("GOAMD64", "1") + if goamd64() != 1 { + t.Errorf("Wrong parsing of GOAMD64=1") + } +} diff --git a/src/internal/cfg/cfg.go b/src/internal/cfg/cfg.go index 815994b679..4cb3fbd4f3 100644 --- a/src/internal/cfg/cfg.go +++ b/src/internal/cfg/cfg.go @@ -33,6 +33,7 @@ const KnownEnv = ` GCCGO GO111MODULE GO386 + GOAMD64 GOARCH GOARM GOBIN diff --git a/test/run.go b/test/run.go index 3fb87af397..7317e8019e 100644 --- a/test/run.go +++ b/test/run.go @@ -1753,7 +1753,7 @@ var ( // are the supported variants. archVariants = map[string][]string{ "386": {"GO386", "sse2", "softfloat"}, - "amd64": {}, + "amd64": {"GOAMD64", "v1", "v2", "v3", "v4"}, "arm": {"GOARM", "5", "6", "7"}, "arm64": {}, "mips": {"GOMIPS", "hardfloat", "softfloat"}, -- cgit v1.2.3-54-g00ecf From 48e2b1ea91171f4fcb56cc521368969e586f033f Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Mon, 13 Sep 2021 16:23:28 -0400 Subject: cmd/compile: fix LocResults formatting When a LocResults is an empty list, it currently prints as ">". Make it print "<>". Change-Id: I0f596791b471d74cd4bbc0059e269708c80592dd Reviewed-on: https://go-review.googlesource.com/c/go/+/350144 Trust: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/location.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go index 252c47cdeb..b575febd72 100644 --- a/src/cmd/compile/internal/ssa/location.go +++ b/src/cmd/compile/internal/ssa/location.go @@ -91,8 +91,8 @@ func (t LocPair) String() string { type LocResults []Location func (t LocResults) String() string { - s := "<" - a := "" + s := "" + a := "<" for _, r := range t { a += s s = "," -- cgit v1.2.3-54-g00ecf From bcdc61d830be61fd5f371f4eb9c345f8dc9ada55 Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 23 Aug 2021 17:19:34 -0400 Subject: cmd/compile: preserve statements better in expandCalls Arg/Load/Dereference rewriting was not using the best Pos for translated values. I also investigated whether OpCopy processing was losing statements, and though they flood the debugging output, doing the "obvious" thing of moving statement marks from copi-er to copy-ee actually makes the resulting binary score slightly worse on statement-boundary measures. (for -N -l, 0.9994 vs 0.9995 from "nostmt -c sqle.test") Fixes #47793. Change-Id: I65cb878d0e5a3ceb5da4ef679020ca5f40e9b02b Reviewed-on: https://go-review.googlesource.com/c/go/+/344769 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Than McIntosh --- src/cmd/compile/internal/ssa/expand_calls.go | 91 ++++++++++++++++------------ src/cmd/compile/internal/ssa/value.go | 10 ++- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index b37d3b8c9c..12c7b16acd 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -176,7 +176,7 @@ func (c *registerCursor) hasRegs() bool { type expandState struct { f *Func abi1 *abi.ABIConfig - debug bool + debug int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both) canSSAType func(*types.Type) bool regSize int64 sp *Value @@ -302,7 +302,7 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error) // // TODO when registers really arrive, must also decompose anything split across two registers or registers and memory. func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset) @@ -325,7 +325,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString()) } - if x.debug { + if x.debug > 1 { x.Printf("---%s, break\n", selector.Op.String()) } case OpArg: @@ -335,7 +335,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString()) } - if x.debug { + if x.debug > 1 { x.Printf("---OpArg, break\n") } break @@ -381,7 +381,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, // This case removes that StructSelect. if leafType != selector.Type { if x.f.Config.SoftFloat && selector.Type.IsFloat() { - if x.debug { + if x.debug > 1 { x.Printf("---OpLoad, break\n") } break // softfloat pass will take care of that @@ -468,7 +468,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, } else { w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call) leaf.copyOf(w) - if x.debug { + if x.debug > 1 { x.Printf("---new %s\n", w.LongString()) } } @@ -687,7 +687,7 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString())) } - if x.debug { + if x.debug > 1 { x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs)) } @@ -836,7 +836,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, // pos and b locate the store instruction, source is the "base" of the value input, // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases. func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String()) @@ -877,7 +877,7 @@ func storeTwoLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t1 // stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg. // If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering. func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("storeArgOrLoad(%s; %s; %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String()) @@ -1060,7 +1060,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t)) s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem) } - if x.debug { + if x.debug > 1 { x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String()) } return s @@ -1071,14 +1071,13 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, // to account for any parameter stores required. // Any of the old Args that have their use count fall to zero are marked OpInvalid. func (x *expandState) rewriteArgs(v *Value, firstArg int) { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg) } // Thread the stores on the memory arg aux := v.Aux.(*AuxCall) - pos := v.Pos.WithNotStmt() m0 := v.MemoryArg() mem := m0 newArgs := []*Value{} @@ -1095,7 +1094,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { } // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move // TODO(register args) this will be more complicated with registers in the picture. - mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, pos) + mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) } else { var rc registerCursor var result *[]*Value @@ -1105,11 +1104,11 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { } else { aOffset = aux.OffsetOfArg(auxI) } - if x.debug { + if x.debug > 1 { x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) } rc.init(aRegs, aux.abiInfo, result, x.sp) - mem = x.storeArgOrLoad(pos, v.Block, a, mem, aType, aOffset, 0, rc) + mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc) } } var preArgStore [2]*Value @@ -1120,16 +1119,31 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { v.AddArg(mem) for _, a := range oldArgs { if a.Uses == 0 { - if x.debug { - x.Printf("...marking %v unused\n", a.LongString()) - } - a.invalidateRecursively() + x.invalidateRecursively(a) } } return } +func (x *expandState) invalidateRecursively(a *Value) { + var s string + if x.debug > 0 { + plus := " " + if a.Pos.IsStmt() == src.PosIsStmt { + plus = " +" + } + s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString() + if x.debug > 1 { + x.Printf("...marking %v unused\n", s) + } + } + lost := a.invalidateRecursively() + if x.debug&1 != 0 && lost { // For odd values of x.debug, do this. + x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s) + } +} + // expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form // that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into // more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are @@ -1148,7 +1162,7 @@ func expandCalls(f *Func) { x := &expandState{ f: f, abi1: f.ABI1, - debug: f.pass.debug > 0, + debug: f.pass.debug, canSSAType: f.fe.CanSSA, regSize: f.Config.RegSize, sp: sp, @@ -1170,7 +1184,7 @@ func expandCalls(f *Func) { x.loRo, x.hiRo = 0, 1 } - if x.debug { + if x.debug > 1 { x.Printf("\nexpandsCalls(%s)\n", f.Name) } @@ -1210,9 +1224,8 @@ func expandCalls(f *Func) { m0 := v.MemoryArg() mem := m0 aux := f.OwnAux - pos := v.Pos.WithNotStmt() allResults := []*Value{} - if x.debug { + if x.debug > 1 { x.Printf("multiValueExit rewriting %s\n", v.LongString()) } var oldArgs []*Value @@ -1233,7 +1246,7 @@ func expandCalls(f *Func) { } continue } - mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos) + mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos) } else { if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr { addr := a.Args[0] // This is a self-move. // TODO(register args) do what here for registers? @@ -1257,13 +1270,13 @@ func expandCalls(f *Func) { b.SetControl(v) for _, a := range oldArgs { if a.Uses == 0 { - if x.debug { + if x.debug > 1 { x.Printf("...marking %v unused\n", a.LongString()) } - a.invalidateRecursively() + x.invalidateRecursively(a) } } - if x.debug { + if x.debug > 1 { x.Printf("...multiValueExit new result %s\n", v.LongString()) } x.indent(-3) @@ -1317,7 +1330,7 @@ func expandCalls(f *Func) { switch w.Op { case OpStructSelect, OpArraySelect, OpSelectN, OpArg: val2Preds[w] += 1 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w]) } } @@ -1326,7 +1339,7 @@ func expandCalls(f *Func) { case OpSelectN: if _, ok := val2Preds[v]; !ok { val2Preds[v] = 0 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) } } @@ -1337,7 +1350,7 @@ func expandCalls(f *Func) { } if _, ok := val2Preds[v]; !ok { val2Preds[v] = 0 - if x.debug { + if x.debug > 1 { x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v]) } } @@ -1451,7 +1464,7 @@ func expandCalls(f *Func) { if dupe == nil { x.commonSelectors[sk] = v } else if x.sdom.IsAncestorEq(dupe.Block, v.Block) { - if x.debug { + if x.debug > 1 { x.Printf("Duplicate, make %s copy of %s\n", v, dupe) } v.copyOf(dupe) @@ -1467,12 +1480,12 @@ func expandCalls(f *Func) { // Rewrite selectors. for i, v := range allOrdered { - if x.debug { + if x.debug > 1 { b := v.Block x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses) } if v.Uses == 0 { - v.invalidateRecursively() + x.invalidateRecursively(v) continue } if v.Op == OpCopy { @@ -1583,7 +1596,7 @@ func expandCalls(f *Func) { v.SetArg(i, aa) for a.Uses == 0 { b := a.Args[0] - a.invalidateRecursively() + x.invalidateRecursively(a) a = b } } @@ -1619,7 +1632,7 @@ func expandCalls(f *Func) { // rewriteArgToMemOrRegs converts OpArg v in-place into the register version of v, // if that is appropriate. func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString()) @@ -1650,7 +1663,7 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { default: panic(badVal("Saw unexpanded OpArg", v)) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", v.LongString()) } return v @@ -1660,7 +1673,7 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value { // or rewrites it into a copy of the appropriate OpArgXXX. The actual OpArgXXX is determined by combining baseArg (an OpArg) // with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers). func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value { - if x.debug { + if x.debug > 1 { x.indent(3) defer x.indent(-3) x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset) @@ -1696,7 +1709,7 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, if toReplace != nil { toReplace.copyOf(w) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", w.LongString()) } return w @@ -1727,7 +1740,7 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, if toReplace != nil { toReplace.copyOf(w) } - if x.debug { + if x.debug > 1 { x.Printf("-->%s\n", w.LongString()) } return w diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 630e4814b9..630143cc50 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -351,11 +351,13 @@ func (v *Value) reset(op Op) { // invalidateRecursively marks a value as invalid (unused) // and after decrementing reference counts on its Args, // also recursively invalidates any of those whose use -// count goes to zero. +// count goes to zero. It returns whether any of the +// invalidated values was marked with IsStmt. // // BEWARE of doing this *before* you've applied intended // updates to SSA. -func (v *Value) invalidateRecursively() { +func (v *Value) invalidateRecursively() bool { + lostStmt := v.Pos.IsStmt() == src.PosIsStmt if v.InCache { v.Block.Func.unCache(v) } @@ -364,7 +366,8 @@ func (v *Value) invalidateRecursively() { for _, a := range v.Args { a.Uses-- if a.Uses == 0 { - a.invalidateRecursively() + lost := a.invalidateRecursively() + lostStmt = lost || lostStmt } } @@ -375,6 +378,7 @@ func (v *Value) invalidateRecursively() { v.AuxInt = 0 v.Aux = nil + return lostStmt } // copyOf is called from rewrite rules. -- cgit v1.2.3-54-g00ecf From d09e09bc615a50e9f2f1144991ebeba08a7c6f05 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 14 Sep 2021 13:37:21 +0800 Subject: cmd/compile: fixing writebarrier.go for -G=3 This is caused by some nodes didn't carry the real line number. Noder1 wraps these node with ir.ParenExpr. To fix this issue, wraps this node like what noder1 does. Change-Id: I212cad09b93b8bf1a7adfad416d229d15711918a Reviewed-on: https://go-review.googlesource.com/c/go/+/349769 Reviewed-by: Matthew Dempsky Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Trust: Keith Randall --- src/cmd/compile/internal/noder/expr.go | 5 +++-- src/cmd/compile/internal/noder/noder.go | 8 ++++++-- src/cmd/compile/internal/noder/stmt.go | 2 +- src/cmd/compile/internal/noder/transform.go | 4 ++++ test/run.go | 2 -- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index 3e1960f7a4..9cd9545b75 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -390,9 +390,10 @@ func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node { } else { key = g.expr(elem.Key) } - exprs[i] = ir.NewKeyExpr(g.pos(elem), key, g.expr(elem.Value)) + value := wrapname(g.pos(elem.Value), g.expr(elem.Value)) + exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value) default: - exprs[i] = g.expr(elem) + exprs[i] = wrapname(g.pos(elem), g.expr(elem)) } } diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 2f18a2f231..7c14fcf041 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -1537,7 +1537,7 @@ func (p *noder) mkname(name *syntax.Name) ir.Node { return mkname(p.name(name)) } -func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { +func wrapname(pos src.XPos, x ir.Node) ir.Node { // These nodes do not carry line numbers. // Introduce a wrapper node to give them the correct line. switch x.Op() { @@ -1547,13 +1547,17 @@ func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { } fallthrough case ir.ONAME, ir.ONONAME, ir.OPACK: - p := ir.NewParenExpr(p.pos(n), x) + p := ir.NewParenExpr(pos, x) p.SetImplicit(true) return p } return x } +func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node { + return wrapname(p.pos(n), x) +} + func (p *noder) setlineno(n syntax.Node) { if n != nil { base.Pos = p.pos(n) diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index 1c366296fc..7f608bb91f 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -37,7 +37,7 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { case *syntax.BlockStmt: return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt)) case *syntax.ExprStmt: - return g.expr(stmt.X) + return wrapname(g.pos(stmt.X), g.expr(stmt.X)) case *syntax.SendStmt: n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value)) if n.Chan.Type().HasTParam() || n.Value.Type().HasTParam() { diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index 91374054b6..a117e310ea 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -404,6 +404,10 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node { return n } + if n.Op() == ir.OPAREN { + n = n.(*ir.ParenExpr).X + } + if types.IdenticalStrict(n.Type(), t) { return n } diff --git a/test/run.go b/test/run.go index 7317e8019e..0c9c8c5cb8 100644 --- a/test/run.go +++ b/test/run.go @@ -2184,8 +2184,6 @@ var types2Failures32Bit = setOf( ) var g3Failures = setOf( - "writebarrier.go", // correct diagnostics, but different lines (probably irgen's fault) - "typeparam/nested.go", // -G=3 doesn't support function-local types with generics ) -- cgit v1.2.3-54-g00ecf From 5402b4376c0c51a31ea9c5ccf16b449a6ab3e79f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 16 Sep 2021 15:00:47 -0700 Subject: spec: fix incorrect type in a shift example Thanks to @bodar (Github) for finding this. Fixes #48422. Change-Id: I031c3d82a02db1d204e2b86b494d89784d37f073 Reviewed-on: https://go-review.googlesource.com/c/go/+/350409 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky --- doc/go_spec.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index 6cc0b796b9..c8051f58af 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -3614,7 +3614,7 @@ var i = 1<<s // 1 has type int var j int32 = 1<<s // 1 has type int32; j == 0 var k = uint64(1<<s) // 1 has type uint64; k == 1<<33 var m int = 1.0<<s // 1.0 has type int; m == 1<<33 -var n = 1.0<<s == j // 1.0 has type int; n == true +var n = 1.0<<s == j // 1.0 has type int32; n == true var o = 1<<s == 2<<s // 1 and 2 have type int; o == false var p = 1<<s == 1<<33 // 1 has type int; p == true var u = 1.0<<s // illegal: 1.0 has type float64, cannot shift -- cgit v1.2.3-54-g00ecf From e09dcc211a338450d3f680fe39abc13b79ddbb29 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 16 Sep 2021 15:09:31 -0700 Subject: go/types, types2: add an additional shift test case The extra test just confirms that the type-checker internally agrees with the spec with the (otherwise invisible) type given to an untyped constant in a specific shift expression. For #48422. Change-Id: I6d98045f90bd20b0cc0a96a147bec9701039cb07 Reviewed-on: https://go-review.googlesource.com/c/go/+/350410 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/types2/api_test.go | 1 + src/go/types/api_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go index 5a20738631..cd5a61332a 100644 --- a/src/cmd/compile/internal/types2/api_test.go +++ b/src/cmd/compile/internal/types2/api_test.go @@ -145,6 +145,7 @@ func TestValuesInfo(t *testing.T) { {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`}, {`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341 + {`package g1; var(j int32; s int; n = 1.0< Date: Sat, 28 Aug 2021 16:06:43 -0700 Subject: testing: skip panics when picking the line number for decoration Fixes #31154 Change-Id: I4cfd98b5e79f1abdc93044fb66855ac2cc0a9a49 Reviewed-on: https://go-review.googlesource.com/c/go/+/345909 Run-TryBot: Caleb Spare TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Carlos Amedee --- src/testing/helper_test.go | 35 +++-------------------------------- src/testing/helperfuncs_test.go | 32 ++++++++++++++++++++++++++++++++ src/testing/testing.go | 3 +++ 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/testing/helper_test.go b/src/testing/helper_test.go index b27fd62ee8..6175410f18 100644 --- a/src/testing/helper_test.go +++ b/src/testing/helper_test.go @@ -33,6 +33,9 @@ helperfuncs_test.go:45: 5 helperfuncs_test.go:21: 6 helperfuncs_test.go:44: 7 helperfuncs_test.go:56: 8 +--- FAIL: Test/sub2 (?s) +helperfuncs_test.go:71: 11 +helperfuncs_test.go:75: recover 12 helperfuncs_test.go:64: 9 helperfuncs_test.go:60: 10 ` @@ -71,38 +74,6 @@ func TestTBHelperParallel(t *T) { } } -func TestTBHelperLineNumer(t *T) { - var buf bytes.Buffer - ctx := newTestContext(1, newMatcher(regexp.MatchString, "", "")) - t1 := &T{ - common: common{ - signal: make(chan bool), - w: &buf, - }, - context: ctx, - } - t1.Run("Test", func(t *T) { - helperA := func(t *T) { - t.Helper() - t.Run("subtest", func(t *T) { - t.Helper() - t.Fatal("fatal error message") - }) - } - helperA(t) - }) - - want := "helper_test.go:92: fatal error message" - got := "" - lines := strings.Split(strings.TrimSpace(buf.String()), "\n") - if len(lines) > 0 { - got = strings.TrimSpace(lines[len(lines)-1]) - } - if got != want { - t.Errorf("got output:\n\n%v\nwant:\n\n%v", got, want) - } -} - type noopWriter int func (nw *noopWriter) Write(b []byte) (int, error) { return len(b), nil } diff --git a/src/testing/helperfuncs_test.go b/src/testing/helperfuncs_test.go index df0476ed73..272b33c0e5 100644 --- a/src/testing/helperfuncs_test.go +++ b/src/testing/helperfuncs_test.go @@ -65,6 +65,14 @@ func testHelper(t *T) { t.Helper() t.Error("9") }) + + // Check that helper-ness propagates up through subtests + // to helpers above. See https://golang.org/issue/44887. + helperSubCallingHelper(t, "11") + + // Check that helper-ness propagates up through panic/recover. + // See https://golang.org/issue/31154. + recoverHelper(t, "12") } func parallelTestHelper(t *T) { @@ -78,3 +86,27 @@ func parallelTestHelper(t *T) { } wg.Wait() } + +func helperSubCallingHelper(t *T, msg string) { + t.Helper() + t.Run("sub2", func(t *T) { + t.Helper() + t.Fatal(msg) + }) +} + +func recoverHelper(t *T, msg string) { + t.Helper() + defer func() { + t.Helper() + if err := recover(); err != nil { + t.Errorf("recover %s", err) + } + }() + doPanic(t, msg) +} + +func doPanic(t *T, msg string) { + t.Helper() + panic(msg) +} diff --git a/src/testing/testing.go b/src/testing/testing.go index a19238d31e..2239e01e22 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -483,6 +483,9 @@ func (c *common) frameSkip(skip int) runtime.Frame { var firstFrame, prevFrame, frame runtime.Frame for more := true; more; prevFrame = frame { frame, more = frames.Next() + if frame.Function == "runtime.gopanic" { + continue + } if frame.Function == c.cleanupName { frames = runtime.CallersFrames(c.cleanupPc) continue -- cgit v1.2.3-54-g00ecf From 265b59aefdd383fc60fcfc7a2838018b16d1d29a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 15 Sep 2021 16:11:37 -0700 Subject: cmd/cgo: for godefs, don't let field prefix removal cause duplicates Fixes #48396 Change-Id: Idd7cb66536ef513806c472d394a929bc271fc26b Reviewed-on: https://go-review.googlesource.com/c/go/+/350159 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor Reviewed-by: Tobias Klauser Reviewed-by: Matt Layher --- misc/cgo/testgodefs/testdata/issue48396.go | 18 ++++++++++++++++++ misc/cgo/testgodefs/testdata/main.go | 3 +++ misc/cgo/testgodefs/testgodefs_test.go | 1 + src/cmd/cgo/gcc.go | 25 +++++++++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 misc/cgo/testgodefs/testdata/issue48396.go diff --git a/misc/cgo/testgodefs/testdata/issue48396.go b/misc/cgo/testgodefs/testdata/issue48396.go new file mode 100644 index 0000000000..d4c192403f --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue48396.go @@ -0,0 +1,18 @@ +// Copyright 2021 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. +// +// +build ignore + +package main + +/* +// from +struct issue48396 { + int fd; + int bpf_fd; +}; +*/ +import "C" + +type Issue48396 C.struct_issue48396 diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go index 4a3f6a701c..5c670f3d32 100644 --- a/misc/cgo/testgodefs/testdata/main.go +++ b/misc/cgo/testgodefs/testdata/main.go @@ -28,6 +28,9 @@ var v7 = S{} // Test that #define'd type is fully defined var _ = issue38649{X: 0} +// Test that prefixes do not cause duplicate field names. +var _ = Issue48396{Fd: 1, Bpf_fd: 2} + func main() { pass := true diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go index aae3404360..7628ffc595 100644 --- a/misc/cgo/testgodefs/testgodefs_test.go +++ b/misc/cgo/testgodefs/testgodefs_test.go @@ -25,6 +25,7 @@ var filePrefixes = []string{ "issue37621", "issue38649", "issue39534", + "issue48396", } func TestGoDefs(t *testing.T) { diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 92adb1ed9c..6b3112b41e 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -3030,6 +3030,31 @@ func upper(s string) string { // so that all fields are exported. func godefsFields(fld []*ast.Field) { prefix := fieldPrefix(fld) + + // Issue 48396: check for duplicate field names. + if prefix != "" { + names := make(map[string]bool) + fldLoop: + for _, f := range fld { + for _, n := range f.Names { + name := n.Name + if name == "_" { + continue + } + if name != prefix { + name = strings.TrimPrefix(n.Name, prefix) + } + name = upper(name) + if names[name] { + // Field name conflict: don't remove prefix. + prefix = "" + break fldLoop + } + names[name] = true + } + } + } + npad := 0 for _, f := range fld { for _, n := range f.Names { -- cgit v1.2.3-54-g00ecf From af9da137a98f897c56d0bfb8e18b177b9ff5af4b Mon Sep 17 00:00:00 2001 From: ian woolf Date: Fri, 20 Aug 2021 10:16:20 +0800 Subject: A+C: update name to real name and add to AUTHORS Change-Id: I857b0e9aa7d933879239ef7ebc0d1a2812a74c25 Reviewed-on: https://go-review.googlesource.com/c/go/+/343533 Reviewed-by: Ian Lance Taylor Trust: Carlos Amedee --- AUTHORS | 1 + CONTRIBUTORS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 95d3158d20..8d8d83605e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1479,6 +1479,7 @@ Zheng Dayu Zhongtao Chen Zhou Peng Ziad Hatahet +Zizhao Zhang Zorion Arrizabalaga Максим Федосеев Роман Хавроненко diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1984d44c53..74d4687373 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1109,7 +1109,6 @@ Ian Lance Taylor Ian Leue Ian Mckay Ian Tay -Ian Woolf Ian Zapolsky Ibrahim AshShohail Icarus Sparry @@ -2749,6 +2748,7 @@ Zhongwei Yao Zhou Peng Ziad Hatahet Ziheng Liu +Zizhao Zhang Zorion Arrizabalaga Zvonimir Pavlinovic Zyad A. Ali -- cgit v1.2.3-54-g00ecf From 8d2a9c32a28838978f5d4e477bbd6db4144005d3 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Sun, 8 Aug 2021 19:44:30 +0930 Subject: all: remove incorrectly repeated words in comments Change-Id: Icbf36e1cd8311b40d18177464e7c41dd8cb1c65b Reviewed-on: https://go-review.googlesource.com/c/go/+/340350 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Carlos Amedee --- src/net/lookup.go | 16 ++++++++-------- src/runtime/mpagealloc.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/net/lookup.go b/src/net/lookup.go index 02beaca7e1..3c0153028c 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -442,7 +442,7 @@ func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) // The returned service names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { return DefaultResolver.LookupSRV(context.Background(), service, proto, name) } @@ -460,7 +460,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err // The returned service names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { cname, addrs, err := r.lookupSRV(ctx, service, proto, name) if err != nil { @@ -490,7 +490,7 @@ func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) ( // The returned mail server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. // // LookupMX uses context.Background internally; to specify the context, use // Resolver.LookupMX. @@ -503,7 +503,7 @@ func LookupMX(name string) ([]*MX, error) { // The returned mail server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { records, err := r.lookupMX(ctx, name) if err != nil { @@ -532,7 +532,7 @@ func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { // The returned name server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. // // LookupNS uses context.Background internally; to specify the context, use // Resolver.LookupNS. @@ -545,7 +545,7 @@ func LookupNS(name string) ([]*NS, error) { // The returned name server names are validated to be properly // formatted presentation-format domain names. If the response contains // invalid names, those records are filtered out and an error -// will be returned alongside the the remaining results, if any. +// will be returned alongside the remaining results, if any. func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { records, err := r.lookupNS(ctx, name) if err != nil { @@ -585,7 +585,7 @@ func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) // // The returned names are validated to be properly formatted presentation-format // domain names. If the response contains invalid names, those records are filtered -// out and an error will be returned alongside the the remaining results, if any. +// out and an error will be returned alongside the remaining results, if any. // // When using the host C library resolver, at most one result will be // returned. To bypass the host resolver, use a custom Resolver. @@ -601,7 +601,7 @@ func LookupAddr(addr string) (names []string, err error) { // // The returned names are validated to be properly formatted presentation-format // domain names. If the response contains invalid names, those records are filtered -// out and an error will be returned alongside the the remaining results, if any. +// out and an error will be returned alongside the remaining results, if any. func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) { names, err := r.lookupAddr(ctx, addr) if err != nil { diff --git a/src/runtime/mpagealloc.go b/src/runtime/mpagealloc.go index 071f1fc274..862882cd82 100644 --- a/src/runtime/mpagealloc.go +++ b/src/runtime/mpagealloc.go @@ -155,7 +155,7 @@ func addrsToSummaryRange(level int, base, limit uintptr) (lo int, hi int) { // upper-bound. Note that the exclusive upper bound may be within a // summary at this level, meaning if we just do the obvious computation // hi will end up being an inclusive upper bound. Unfortunately, just - // adding 1 to that is too broad since we might be on the very edge of + // adding 1 to that is too broad since we might be on the very edge // of a summary's max page count boundary for this level // (1 << levelLogPages[level]). So, make limit an inclusive upper bound // then shift, then add 1, so we get an exclusive upper bound at the end. -- cgit v1.2.3-54-g00ecf From 14e812bfc575400a02e9e7536344a3f78a6cba08 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 16 Sep 2021 16:11:19 -0600 Subject: syscall: do not use handle lists on windows when NoInheritHandles is true If NoInheritHandles is passed, then we shouldn't attempt to do anything with handle lists. Otherwise CreateProcess fails with invalid param, because it's being told both to not inherit handles and to inherit certain handles. This commit fixes that by using the same logic for handle lists as it does for enabling or disabling handle inheritance. It also adds a test to make sure this doesn't regress again. Fixes #48040 Change-Id: I507261baeec263091738ab90157a991d917dc92f Reviewed-on: https://go-review.googlesource.com/c/go/+/350411 Reviewed-by: Patrik Nyblom Trust: Jason A. Donenfeld Run-TryBot: Jason A. Donenfeld TryBot-Result: Go Bot --- src/os/exec/exec_windows_test.go | 14 ++++++++++++++ src/syscall/exec_windows.go | 8 +++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/os/exec/exec_windows_test.go b/src/os/exec/exec_windows_test.go index fbccffec0e..bd4dfb31da 100644 --- a/src/os/exec/exec_windows_test.go +++ b/src/os/exec/exec_windows_test.go @@ -10,6 +10,7 @@ package exec_test import ( "io" "os" + "os/exec" "strconv" "syscall" "testing" @@ -41,3 +42,16 @@ func TestPipePassing(t *testing.T) { t.Error(err) } } + +func TestNoInheritHandles(t *testing.T) { + cmd := exec.Command("cmd", "/c exit 88") + cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true} + err := cmd.Run() + exitError, ok := err.(*exec.ExitError) + if !ok { + t.Fatalf("got error %v; want ExitError", err) + } + if exitError.ExitCode() != 88 { + t.Fatalf("got exit code %d; want 88", exitError.ExitCode()) + } +} diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go index 18d15028c3..9d10d6a512 100644 --- a/src/syscall/exec_windows.go +++ b/src/syscall/exec_windows.go @@ -390,8 +390,10 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle } fd = fd[:j] + willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles + // Do not accidentally inherit more than these handles. - if len(fd) > 0 { + if willInheritHandles { err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil) if err != nil { return 0, 0, err @@ -401,9 +403,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle pi := new(ProcessInformation) flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT if sys.Token != 0 { - err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } else { - err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, len(fd) > 0 && !sys.NoInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) + err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi) } if err != nil { return 0, 0, err -- cgit v1.2.3-54-g00ecf From 6602c86a38ff0d8889257e908489162de38ebbd8 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 14 Sep 2021 02:21:22 +1000 Subject: cmd/internal/obj/riscv: improve instruction validation Ensure that rs2 is none for various instruction encodings. Fix a couple of cases where it should have been but is not. Change-Id: I9f8211a0257e49643dbbc89e158e048050ebe6f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/349649 Trust: Joel Sing Reviewed-by: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot --- src/cmd/internal/obj/riscv/obj.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 62d44d8a3f..f0ea21de97 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -1003,6 +1003,7 @@ func validateII(ctxt *obj.Link, ins *instruction) { wantImmI(ctxt, ins.as, ins.imm, 12) wantIntReg(ctxt, ins.as, "rd", ins.rd) wantIntReg(ctxt, ins.as, "rs1", ins.rs1) + wantNoneReg(ctxt, ins.as, "rs2", ins.rs2) wantNoneReg(ctxt, ins.as, "rs3", ins.rs3) } @@ -1010,6 +1011,7 @@ func validateIF(ctxt *obj.Link, ins *instruction) { wantImmI(ctxt, ins.as, ins.imm, 12) wantFloatReg(ctxt, ins.as, "rd", ins.rd) wantIntReg(ctxt, ins.as, "rs1", ins.rs1) + wantNoneReg(ctxt, ins.as, "rs2", ins.rs2) wantNoneReg(ctxt, ins.as, "rs3", ins.rs3) } @@ -1017,6 +1019,7 @@ func validateSI(ctxt *obj.Link, ins *instruction) { wantImmI(ctxt, ins.as, ins.imm, 12) wantIntReg(ctxt, ins.as, "rd", ins.rd) wantIntReg(ctxt, ins.as, "rs1", ins.rs1) + wantNoneReg(ctxt, ins.as, "rs2", ins.rs2) wantNoneReg(ctxt, ins.as, "rs3", ins.rs3) } @@ -1024,6 +1027,7 @@ func validateSF(ctxt *obj.Link, ins *instruction) { wantImmI(ctxt, ins.as, ins.imm, 12) wantIntReg(ctxt, ins.as, "rd", ins.rd) wantFloatReg(ctxt, ins.as, "rs1", ins.rs1) + wantNoneReg(ctxt, ins.as, "rs2", ins.rs2) wantNoneReg(ctxt, ins.as, "rs3", ins.rs3) } @@ -1567,7 +1571,7 @@ func instructionForProg(p *obj.Prog) *instruction { func instructionsForOpImmediate(p *obj.Prog, as obj.As, rs int16) []*instruction { // $imm, REG, TO ins := instructionForProg(p) - ins.as, ins.rs1 = as, uint32(rs) + ins.as, ins.rs1, ins.rs2 = as, uint32(rs), obj.REG_NONE low, high, err := Split32BitImmediate(ins.imm) if err != nil { @@ -1990,7 +1994,7 @@ func instructionsForProg(p *obj.Prog) []*instruction { case ASEQZ: // SEQZ rs, rd -> SLTIU $1, rs, rd ins.as = ASLTIU - ins.rs1 = uint32(p.From.Reg) + ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE ins.imm = 1 case ASNEZ: -- cgit v1.2.3-54-g00ecf From 6d02ce85840fb7a1a9239d02e02710a008b1657a Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Fri, 17 Sep 2021 13:48:39 +0200 Subject: runtime: fix prettyprinting of parametric types in gdb golang.org/cl/344929 broke the minimal functionality that the python pretty printer for GDB had, this change restores it to its status prior to that CL. Change-Id: I4c7141d4ff726d224a074ecc533d0f896fc0052c Reviewed-on: https://go-review.googlesource.com/c/go/+/350529 TryBot-Result: Go Bot Reviewed-by: David Chase Trust: Than McIntosh --- src/runtime/runtime-gdb.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/runtime/runtime-gdb.py b/src/runtime/runtime-gdb.py index 8d96dfb609..5bb605cc37 100644 --- a/src/runtime/runtime-gdb.py +++ b/src/runtime/runtime-gdb.py @@ -219,6 +219,9 @@ class ChanTypePrinter: yield ('[{0}]'.format(i), (ptr + j).dereference()) +def paramtypematch(t, pattern): + return t.code == gdb.TYPE_CODE_TYPEDEF and str(t).startswith(".param") and pattern.match(str(t.target())) + # # Register all the *Printer classes above. # @@ -228,6 +231,8 @@ def makematcher(klass): try: if klass.pattern.match(str(val.type)): return klass(val) + elif paramtypematch(val.type, klass.pattern): + return klass(val.cast(val.type.target())) except Exception: pass return matcher @@ -387,7 +392,7 @@ class GoLenFunc(gdb.Function): def invoke(self, obj): typename = str(obj.type) for klass, fld in self.how: - if klass.pattern.match(typename): + if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern): return obj[fld] @@ -402,7 +407,7 @@ class GoCapFunc(gdb.Function): def invoke(self, obj): typename = str(obj.type) for klass, fld in self.how: - if klass.pattern.match(typename): + if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern): return obj[fld] -- cgit v1.2.3-54-g00ecf From 70493b3eb06d10217d9aa346ffff0b4c4f2cc72b Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Fri, 16 Jul 2021 02:56:08 +1000 Subject: runtime/cgo: save and restore X3 (aka GP) for crosscall1 on riscv64 The C code that is calling crosscall1 may depend on the GP register, which Go code will currently clobber. Save and restore both X3 (aka GP) and X4 (aka TP) in this code path (note that the Go code does not currently clobber X4, however there is minimal downside to saving and restoring it here, which then also matches crosscall2). Updates #47100 Change-Id: Icbb706d7889d5dc59de3efb2b510fa6ea2069496 Reviewed-on: https://go-review.googlesource.com/c/go/+/334870 Trust: Joel Sing Trust: Meng Zhuo Reviewed-by: Meng Zhuo Reviewed-by: Ian Lance Taylor Run-TryBot: Meng Zhuo --- src/runtime/cgo/gcc_riscv64.S | 108 ++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/runtime/cgo/gcc_riscv64.S b/src/runtime/cgo/gcc_riscv64.S index f429dc64ee..fdc77496d9 100644 --- a/src/runtime/cgo/gcc_riscv64.S +++ b/src/runtime/cgo/gcc_riscv64.S @@ -8,36 +8,38 @@ * Calling into the gc tool chain, where all registers are caller save. * Called from standard RISCV ELF psABI, where x8-x9, x18-x27, f8-f9 and * f18-f27 are callee-save, so they must be saved explicitly, along with - * x1 (LR). + * x1 (LR), x3 (GP) and x4 (TP). */ .globl crosscall1 crosscall1: - sd x1, -200(sp) - addi sp, sp, -200 - sd x8, 8(sp) - sd x9, 16(sp) - sd x18, 24(sp) - sd x19, 32(sp) - sd x20, 40(sp) - sd x21, 48(sp) - sd x22, 56(sp) - sd x23, 64(sp) - sd x24, 72(sp) - sd x25, 80(sp) - sd x26, 88(sp) - sd x27, 96(sp) - fsd f8, 104(sp) - fsd f9, 112(sp) - fsd f18, 120(sp) - fsd f19, 128(sp) - fsd f20, 136(sp) - fsd f21, 144(sp) - fsd f22, 152(sp) - fsd f23, 160(sp) - fsd f24, 168(sp) - fsd f25, 176(sp) - fsd f26, 184(sp) - fsd f27, 192(sp) + sd x1, -216(sp) + addi sp, sp, -216 + sd x3, 8(sp) + sd x4, 16(sp) + sd x8, 24(sp) + sd x9, 32(sp) + sd x18, 40(sp) + sd x19, 48(sp) + sd x20, 56(sp) + sd x21, 64(sp) + sd x22, 72(sp) + sd x23, 80(sp) + sd x24, 88(sp) + sd x25, 96(sp) + sd x26, 104(sp) + sd x27, 112(sp) + fsd f8, 120(sp) + fsd f9, 128(sp) + fsd f18, 136(sp) + fsd f19, 144(sp) + fsd f20, 152(sp) + fsd f21, 160(sp) + fsd f22, 168(sp) + fsd f23, 176(sp) + fsd f24, 184(sp) + fsd f25, 192(sp) + fsd f26, 200(sp) + fsd f27, 208(sp) // a0 = *fn, a1 = *setg_gcc, a2 = *g mv s1, a0 @@ -47,31 +49,33 @@ crosscall1: jalr ra, s1 // call fn ld x1, 0(sp) - ld x8, 8(sp) - ld x9, 16(sp) - ld x18, 24(sp) - ld x19, 32(sp) - ld x20, 40(sp) - ld x21, 48(sp) - ld x22, 56(sp) - ld x23, 64(sp) - ld x24, 72(sp) - ld x25, 80(sp) - ld x26, 88(sp) - ld x27, 96(sp) - fld f8, 104(sp) - fld f9, 112(sp) - fld f18, 120(sp) - fld f19, 128(sp) - fld f20, 136(sp) - fld f21, 144(sp) - fld f22, 152(sp) - fld f23, 160(sp) - fld f24, 168(sp) - fld f25, 176(sp) - fld f26, 184(sp) - fld f27, 192(sp) - addi sp, sp, 200 + ld x3, 8(sp) + ld x4, 16(sp) + ld x8, 24(sp) + ld x9, 32(sp) + ld x18, 40(sp) + ld x19, 48(sp) + ld x20, 56(sp) + ld x21, 64(sp) + ld x22, 72(sp) + ld x23, 80(sp) + ld x24, 88(sp) + ld x25, 96(sp) + ld x26, 104(sp) + ld x27, 112(sp) + fld f8, 120(sp) + fld f9, 128(sp) + fld f18, 136(sp) + fld f19, 144(sp) + fld f20, 152(sp) + fld f21, 160(sp) + fld f22, 168(sp) + fld f23, 176(sp) + fld f24, 184(sp) + fld f25, 192(sp) + fld f26, 200(sp) + fld f27, 208(sp) + addi sp, sp, 216 jr ra -- cgit v1.2.3-54-g00ecf From 7f36ef0aff702f2598390d0349f9c9632942d40b Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Fri, 17 Sep 2021 11:12:31 -0700 Subject: cmd/compile/internal/noder: hide TestUnifiedCompare behind -cmp flag This test is fragile and is starting to impede others' work. This CL disables it until I have time to either find a solution for the issues or decide to just delete the test altogether. Change-Id: Icefabb6d3fbedec5d16536de78be4ca20d63133c Reviewed-on: https://go-review.googlesource.com/c/go/+/350729 Trust: Matthew Dempsky Trust: Dan Scales Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Dan Scales --- src/cmd/compile/internal/noder/unified_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cmd/compile/internal/noder/unified_test.go b/src/cmd/compile/internal/noder/unified_test.go index 96cc66f775..d7334df282 100644 --- a/src/cmd/compile/internal/noder/unified_test.go +++ b/src/cmd/compile/internal/noder/unified_test.go @@ -16,6 +16,7 @@ import ( ) var ( + flagCmp = flag.Bool("cmp", false, "enable TestUnifiedCompare") flagPkgs = flag.String("pkgs", "std", "list of packages to compare (ignored in -short mode)") flagAll = flag.Bool("all", false, "enable testing of all GOOS/GOARCH targets") flagParallel = flag.Bool("parallel", false, "test GOOS/GOARCH targets in parallel") @@ -37,6 +38,12 @@ var ( // command's -run flag for subtest matching is recommended for less // powerful machines. func TestUnifiedCompare(t *testing.T) { + // TODO(mdempsky): Either re-enable or delete. Disabled for now to + // avoid impeding others' forward progress. + if !*flagCmp { + t.Skip("skipping TestUnifiedCompare (use -cmp to enable)") + } + targets, err := exec.Command("go", "tool", "dist", "list").Output() if err != nil { t.Fatal(err) -- cgit v1.2.3-54-g00ecf From 323c6f74d35ec29baac2a1aba4270f89b022815a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 9 Sep 2021 14:49:41 -0700 Subject: log: don't format if writing to io.Discard Fixes #47164 Change-Id: Ied03842360be4c86f1d9ead816f12c057a1f8dad Reviewed-on: https://go-review.googlesource.com/c/go/+/348741 Trust: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Emmanuel Odeke Reviewed-by: Valentin Deleplace --- src/log/log.go | 53 ++++++++++++++++++++++++++++++++++++++++++----------- src/log/log_test.go | 14 +++++++++++++- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/log/log.go b/src/log/log.go index b77af29032..3172384718 100644 --- a/src/log/log.go +++ b/src/log/log.go @@ -20,6 +20,7 @@ import ( "os" "runtime" "sync" + "sync/atomic" "time" ) @@ -50,11 +51,12 @@ const ( // the Writer's Write method. A Logger can be used simultaneously from // multiple goroutines; it guarantees to serialize access to the Writer. type Logger struct { - mu sync.Mutex // ensures atomic writes; protects the following fields - prefix string // prefix on each line to identify the logger (but see Lmsgprefix) - flag int // properties - out io.Writer // destination for output - buf []byte // for accumulating text to write + mu sync.Mutex // ensures atomic writes; protects the following fields + prefix string // prefix on each line to identify the logger (but see Lmsgprefix) + flag int // properties + out io.Writer // destination for output + buf []byte // for accumulating text to write + isDiscard int32 // atomic boolean: whether out == io.Discard } // New creates a new Logger. The out variable sets the @@ -63,7 +65,11 @@ type Logger struct { // after the log header if the Lmsgprefix flag is provided. // The flag argument defines the logging properties. func New(out io.Writer, prefix string, flag int) *Logger { - return &Logger{out: out, prefix: prefix, flag: flag} + l := &Logger{out: out, prefix: prefix, flag: flag} + if out == io.Discard { + l.isDiscard = 1 + } + return l } // SetOutput sets the output destination for the logger. @@ -71,6 +77,11 @@ func (l *Logger) SetOutput(w io.Writer) { l.mu.Lock() defer l.mu.Unlock() l.out = w + isDiscard := int32(0) + if w == io.Discard { + isDiscard = 1 + } + atomic.StoreInt32(&l.isDiscard, isDiscard) } var std = New(os.Stderr, "", LstdFlags) @@ -188,16 +199,29 @@ func (l *Logger) Output(calldepth int, s string) error { // Printf calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Printf. func (l *Logger) Printf(format string, v ...interface{}) { + if atomic.LoadInt32(&l.isDiscard) != 0 { + return + } l.Output(2, fmt.Sprintf(format, v...)) } // Print calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Print. -func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) } +func (l *Logger) Print(v ...interface{}) { + if atomic.LoadInt32(&l.isDiscard) != 0 { + return + } + l.Output(2, fmt.Sprint(v...)) +} // Println calls l.Output to print to the logger. // Arguments are handled in the manner of fmt.Println. -func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) } +func (l *Logger) Println(v ...interface{}) { + if atomic.LoadInt32(&l.isDiscard) != 0 { + return + } + l.Output(2, fmt.Sprintln(v...)) +} // Fatal is equivalent to l.Print() followed by a call to os.Exit(1). func (l *Logger) Fatal(v ...interface{}) { @@ -277,9 +301,7 @@ func (l *Logger) Writer() io.Writer { // SetOutput sets the output destination for the standard logger. func SetOutput(w io.Writer) { - std.mu.Lock() - defer std.mu.Unlock() - std.out = w + std.SetOutput(w) } // Flags returns the output flags for the standard logger. @@ -314,18 +336,27 @@ func Writer() io.Writer { // Print calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Print. func Print(v ...interface{}) { + if atomic.LoadInt32(&std.isDiscard) != 0 { + return + } std.Output(2, fmt.Sprint(v...)) } // Printf calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Printf. func Printf(format string, v ...interface{}) { + if atomic.LoadInt32(&std.isDiscard) != 0 { + return + } std.Output(2, fmt.Sprintf(format, v...)) } // Println calls Output to print to the standard logger. // Arguments are handled in the manner of fmt.Println. func Println(v ...interface{}) { + if atomic.LoadInt32(&std.isDiscard) != 0 { + return + } std.Output(2, fmt.Sprintln(v...)) } diff --git a/src/log/log_test.go b/src/log/log_test.go index 5be8e82258..938ed42357 100644 --- a/src/log/log_test.go +++ b/src/log/log_test.go @@ -9,6 +9,7 @@ package log import ( "bytes" "fmt" + "io" "os" "regexp" "strings" @@ -20,7 +21,7 @@ const ( Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]` Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]` Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]` - Rline = `(60|62):` // must update if the calls to l.Printf / l.Print below move + Rline = `(61|63):` // must update if the calls to l.Printf / l.Print below move Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline ) @@ -179,6 +180,17 @@ func TestEmptyPrintCreatesLine(t *testing.T) { } } +func TestDiscard(t *testing.T) { + l := New(io.Discard, "", 0) + s := strings.Repeat("a", 102400) + c := testing.AllocsPerRun(100, func() { l.Printf("%s", s) }) + // One allocation for slice passed to Printf, + // but none for formatting of long string. + if c > 1 { + t.Errorf("got %v allocs, want at most 1", c) + } +} + func BenchmarkItoa(b *testing.B) { dst := make([]byte, 0, 64) for i := 0; i < b.N; i++ { -- cgit v1.2.3-54-g00ecf From 74e384f50d3071c97effa3afd43ec29111587d59 Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Wed, 15 Sep 2021 12:37:20 +0800 Subject: internal/poll: inject a hook into the runtime finalizer to count the closed pipes Fixes #48066 Change-Id: Icd6974dfcc496c054bb096e5d70de6e135984517 Reviewed-on: https://go-review.googlesource.com/c/go/+/349774 Reviewed-by: Bryan C. Mills Reviewed-by: Ian Lance Taylor Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills TryBot-Result: Go Bot --- src/internal/poll/splice_linux_test.go | 60 ++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/internal/poll/splice_linux_test.go b/src/internal/poll/splice_linux_test.go index deac5c3759..8c4363886e 100644 --- a/src/internal/poll/splice_linux_test.go +++ b/src/internal/poll/splice_linux_test.go @@ -6,40 +6,48 @@ package poll_test import ( "internal/poll" - "internal/syscall/unix" "runtime" - "syscall" + "sync" + "sync/atomic" "testing" "time" ) -// checkPipes returns true if all pipes are closed properly, false otherwise. -func checkPipes(fds []int) bool { - for _, fd := range fds { - // Check if each pipe fd has been closed. - _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0) - if errno == 0 { - return false +var closeHook atomic.Value // func(fd int) + +func init() { + closeFunc := poll.CloseFunc + poll.CloseFunc = func(fd int) (err error) { + if v := closeHook.Load(); v != nil { + if hook := v.(func(int)); hook != nil { + hook(fd) + } } + return closeFunc(fd) } - return true } func TestSplicePipePool(t *testing.T) { const N = 64 var ( - p *poll.SplicePipe - ps []*poll.SplicePipe - fds []int - err error + p *poll.SplicePipe + ps []*poll.SplicePipe + allFDs []int + pendingFDs sync.Map // fd → struct{}{} + err error ) + + closeHook.Store(func(fd int) { pendingFDs.Delete(fd) }) + t.Cleanup(func() { closeHook.Store((func(int))(nil)) }) + for i := 0; i < N; i++ { p, _, err = poll.GetPipe() if err != nil { - t.Skip("failed to create pipe, skip this test") + t.Skipf("failed to create pipe due to error(%v), skip this test", err) } _, pwfd := poll.GetPipeFds(p) - fds = append(fds, pwfd) + allFDs = append(allFDs, pwfd) + pendingFDs.Store(pwfd, struct{}{}) ps = append(ps, p) } for _, p = range ps { @@ -62,19 +70,21 @@ func TestSplicePipePool(t *testing.T) { for { runtime.GC() time.Sleep(10 * time.Millisecond) - if checkPipes(fds) { + + // Detect whether all pipes are closed properly. + var leakedFDs []int + pendingFDs.Range(func(k, v interface{}) bool { + leakedFDs = append(leakedFDs, k.(int)) + return true + }) + if len(leakedFDs) == 0 { break } + select { case <-expiredTime.C: - t.Logf("descriptors to check: %v", fds) - for _, fd := range fds { - _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0) - if errno == 0 { - t.Errorf("descriptor %d still open", fd) - } - } - t.Fatal("at least one pipe is still open") + t.Logf("all descriptors: %v", allFDs) + t.Fatalf("leaked descriptors: %v", leakedFDs) default: } } -- cgit v1.2.3-54-g00ecf From cea7a71d40115333d5943162e5764162b767389d Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 13 Sep 2021 18:50:19 -0700 Subject: cmd/compile: fix generic type handling in crawler There are a bunch of nodes beside ONAME and OTYPE, (such as OSTRUCTLIT and OCOMPLIT) which can introduce a generic type that we need to mark. So, just mark any generic type on any node in markInlBody. In this particular issue, the type is introduced by an OSTRUCTLIT node. Updates #48337 Change-Id: I271932518f0c1fb54d91a603e01a855c69df631d Reviewed-on: https://go-review.googlesource.com/c/go/+/349909 Trust: Dan Scales Trust: Carlos Amedee Reviewed-by: Keith Randall --- src/cmd/compile/internal/typecheck/crawler.go | 45 +++++++++++++-------------- test/typeparam/issue48337a.dir/a.go | 32 +++++++++++++++++++ test/typeparam/issue48337a.dir/main.go | 12 +++++++ test/typeparam/issue48337a.go | 7 +++++ test/typeparam/issue48337a.out | 1 + 5 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 test/typeparam/issue48337a.dir/a.go create mode 100644 test/typeparam/issue48337a.dir/main.go create mode 100644 test/typeparam/issue48337a.go create mode 100644 test/typeparam/issue48337a.out diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go index 9e523c3d14..3f212aa805 100644 --- a/src/cmd/compile/internal/typecheck/crawler.go +++ b/src/cmd/compile/internal/typecheck/crawler.go @@ -44,12 +44,13 @@ func (p *crawler) markObject(n *ir.Name) { p.markType(n.Type()) } -// markType recursively visits types reachable from t to identify -// functions whose inline bodies may be needed. +// markType recursively visits types reachable from t to identify functions whose +// inline bodies may be needed. For instantiated generic types, it visits the base +// generic type, which has the relevant methods. func (p *crawler) markType(t *types.Type) { - if t.IsInstantiatedGeneric() { - // Re-instantiated types don't add anything new, so don't follow them. - return + if t.OrigSym() != nil { + // Convert to the base generic type. + t = t.OrigSym().Def.Type() } if p.marked[t] { return @@ -92,6 +93,9 @@ func (p *crawler) markType(t *types.Type) { p.markType(t.Elem()) case types.TSTRUCT: + if t.IsFuncArgStruct() { + break + } for _, f := range t.FieldSlice() { if types.IsExported(f.Sym.Name) || f.Embedded != 0 { p.markType(f.Type) @@ -129,9 +133,9 @@ func (p *crawler) markEmbed(t *types.Type) { t = t.Elem() } - if t.IsInstantiatedGeneric() { - // Re-instantiated types don't add anything new, so don't follow them. - return + if t.OrigSym() != nil { + // Convert to the base generic type. + t = t.OrigSym().Def.Type() } if p.embedded[t] { @@ -185,6 +189,15 @@ func (p *crawler) markInlBody(n *ir.Name) { var doFlood func(n ir.Node) doFlood = func(n ir.Node) { + t := n.Type() + if t != nil && (t.HasTParam() || t.IsFullyInstantiated()) { + // Ensure that we call markType() on any base generic type + // that is written to the export file (even if not explicitly + // marked for export), so we will call markInlBody on its + // methods, and the methods will be available for + // instantiation if needed. + p.markType(t) + } switch n.Op() { case ir.OMETHEXPR, ir.ODOTMETH: p.markInlBody(ir.MethodExprName(n)) @@ -198,9 +211,6 @@ func (p *crawler) markInlBody(n *ir.Name) { case ir.PEXTERN: Export(n) } - p.checkGenericType(n.Type()) - case ir.OTYPE: - p.checkGenericType(n.Type()) case ir.OMETHVALUE: // Okay, because we don't yet inline indirect // calls to method values. @@ -216,16 +226,3 @@ func (p *crawler) markInlBody(n *ir.Name) { // because after inlining they might be callable. ir.VisitList(fn.Inl.Body, doFlood) } - -// checkGenerictype ensures that we call markType() on any base generic type that -// is written to the export file (even if not explicitly marked -// for export), so its methods will be available for inlining if needed. -func (p *crawler) checkGenericType(t *types.Type) { - if t != nil && t.HasTParam() { - if t.OrigSym() != nil { - // Convert to the base generic type. - t = t.OrigSym().Def.Type() - } - p.markType(t) - } -} diff --git a/test/typeparam/issue48337a.dir/a.go b/test/typeparam/issue48337a.dir/a.go new file mode 100644 index 0000000000..6f1b128589 --- /dev/null +++ b/test/typeparam/issue48337a.dir/a.go @@ -0,0 +1,32 @@ +// Copyright 2021 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 a + +import ( + "fmt" + "sync" +) + +type WrapperWithLock[T any] interface { + PrintWithLock() +} + +func NewWrapperWithLock[T any](value T) WrapperWithLock[T] { + return &wrapperWithLock[T]{ + Object: value, + } +} + +type wrapperWithLock[T any] struct { + Lock sync.Mutex + Object T +} + +func (w *wrapperWithLock[T]) PrintWithLock() { + w.Lock.Lock() + defer w.Lock.Unlock() + + fmt.Println(w.Object) +} diff --git a/test/typeparam/issue48337a.dir/main.go b/test/typeparam/issue48337a.dir/main.go new file mode 100644 index 0000000000..16f71153f3 --- /dev/null +++ b/test/typeparam/issue48337a.dir/main.go @@ -0,0 +1,12 @@ +// Copyright 2021 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 main + +import "a" + +func main() { + obj := a.NewWrapperWithLock("this file does import sync") + obj.PrintWithLock() +} diff --git a/test/typeparam/issue48337a.go b/test/typeparam/issue48337a.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/issue48337a.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored diff --git a/test/typeparam/issue48337a.out b/test/typeparam/issue48337a.out new file mode 100644 index 0000000000..fa8d3eedcb --- /dev/null +++ b/test/typeparam/issue48337a.out @@ -0,0 +1 @@ +this file does import sync -- cgit v1.2.3-54-g00ecf From 1a49dcb82f1ef0d028f74f4fd955ee01b09b466d Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 17 Sep 2021 14:23:04 +0200 Subject: syscall: remove //sysnb comment generating Setreuid for linux/arm64 CL 210639 moved the //sysnb for Setreuid from syscall_linux_$GOARCH.go to syscall_linux.go but forgot to remove the comment from syscall_linux_arm64.go which leads to Setreuid being generated twice for linux/arm64. Remove that //sysnb comment to avoid this. Change-Id: I2c8ad95f786530ca964685b0a4fe463c64764307 Reviewed-on: https://go-review.googlesource.com/c/go/+/350531 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/syscall/syscall_linux_arm64.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index f575c84c93..517723ae47 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -42,7 +42,6 @@ func EpollCreate(size int) (fd int, err error) { //sys Setfsgid(gid int) (err error) //sys Setfsuid(uid int) (err error) //sysnb setrlimit(resource int, rlim *Rlimit) (err error) -//sysnb Setreuid(ruid int, euid int) (err error) //sys Shutdown(fd int, how int) (err error) //sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) -- cgit v1.2.3-54-g00ecf From 974b0166d6a7c20b98f7e517e49197bea46fc5e2 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 17 Sep 2021 14:25:54 +0200 Subject: syscall: implement Pipe using pipe2 syscall on all linux platforms Most architectures currently already implement Pipe using the pipe2 syscall. Only 386, amd64 and mips{,le} still use the pipe syscall. However, some systems (e.g. Android seccomp policies) block that syscall, see #40828 for an example. The pipe2 syscall was added in Linux kernel version 2.6.27. The minimum required Linux kernel version for Go 1.18 will be changed to 2.6.32 per #45964 so it is possible to unify the implementation of Pipe using the pipe2 syscall. For #45964 Change-Id: I8ed6a391300c95f3107b4ec6b27d320e42fb535b Reviewed-on: https://go-review.googlesource.com/c/go/+/350530 Trust: Tobias Klauser Run-TryBot: Tobias Klauser TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- src/syscall/syscall_linux.go | 17 +++++++++++++++++ src/syscall/syscall_linux_386.go | 26 -------------------------- src/syscall/syscall_linux_amd64.go | 26 -------------------------- src/syscall/syscall_linux_arm.go | 26 -------------------------- src/syscall/syscall_linux_arm64.go | 24 ------------------------ src/syscall/syscall_linux_mips64x.go | 24 ------------------------ src/syscall/syscall_linux_mipsx.go | 23 ----------------------- src/syscall/syscall_linux_ppc64x.go | 24 ------------------------ src/syscall/syscall_linux_riscv64.go | 24 ------------------------ src/syscall/syscall_linux_s390x.go | 24 ------------------------ src/syscall/zsyscall_linux_386.go | 30 ++++++++++-------------------- src/syscall/zsyscall_linux_amd64.go | 30 ++++++++++-------------------- src/syscall/zsyscall_linux_arm.go | 30 ++++++++++-------------------- src/syscall/zsyscall_linux_arm64.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_mips.go | 32 ++++++++++---------------------- src/syscall/zsyscall_linux_mips64.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_mips64le.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_mipsle.go | 32 ++++++++++---------------------- src/syscall/zsyscall_linux_ppc64.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_ppc64le.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_riscv64.go | 20 ++++++++++---------- src/syscall/zsyscall_linux_s390x.go | 20 ++++++++++---------- 22 files changed, 137 insertions(+), 395 deletions(-) diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index 6d428d58dd..f02fa45a89 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -161,6 +161,23 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) return openat(dirfd, path, flags|O_LARGEFILE, mode) } +func Pipe(p []int) error { + return Pipe2(p, 0) +} + +//sysnb pipe2(p *[2]_C_int, flags int) (err error) + +func Pipe2(p []int, flags int) error { + if len(p) != 2 { + return EINVAL + } + var pp [2]_C_int + err := pipe2(&pp, flags) + p[0] = int(pp[0]) + p[1] = int(pp[1]) + return err +} + //sys readlinkat(dirfd int, path string, buf []byte) (n int, err error) func Readlink(path string, buf []byte) (n int, err error) { diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go index 0db037470d..98442055d8 100644 --- a/src/syscall/syscall_linux_386.go +++ b/src/syscall/syscall_linux_386.go @@ -22,32 +22,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // 64-bit file system and 32-bit uid calls // (386 default is 32-bit file system and 16-bit uid). //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go index 5df3f796d1..04acd063fa 100644 --- a/src/syscall/syscall_linux_amd64.go +++ b/src/syscall/syscall_linux_amd64.go @@ -110,32 +110,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe(&pp) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Rip } func (r *PtraceRegs) SetPC(pc uint64) { r.Rip = pc } diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index fffa4b29b9..f2f342e7ed 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -22,32 +22,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe(p *[2]_C_int) (err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Underlying system call writes to newoffset via pointer. // Implemented in assembly to avoid allocation. func seek(fd int, offset int64, whence int) (newoffset int64, err Errno) diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index 517723ae47..990e732f35 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -145,30 +145,6 @@ func utimes(path string, tv *[2]Timeval) (err error) { return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Getrlimit prefers the prlimit64 system call. See issue 38604. func Getrlimit(resource int, rlim *Rlimit) error { err := prlimit(0, resource, nil, rlim) diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go index 5feb03e915..fa0d2799ed 100644 --- a/src/syscall/syscall_linux_mips64x.go +++ b/src/syscall/syscall_linux_mips64x.go @@ -103,30 +103,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func Ioperm(from int, num int, on int) (err error) { return ENOSYS } diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go index 39104d71d8..568523eb27 100644 --- a/src/syscall/syscall_linux_mipsx.go +++ b/src/syscall/syscall_linux_mipsx.go @@ -112,29 +112,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: int32(sec), Usec: int32(usec)} } -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe() (p1 int, p2 int, err error) - -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - p[0], p[1], err = pipe() - return -} - //sys mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go index 495ae29757..3e73f6f2a4 100644 --- a/src/syscall/syscall_linux_ppc64x.go +++ b/src/syscall/syscall_linux_ppc64x.go @@ -82,30 +82,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Nip } func (r *PtraceRegs) SetPC(pc uint64) { r.Nip = pc } diff --git a/src/syscall/syscall_linux_riscv64.go b/src/syscall/syscall_linux_riscv64.go index 2a0fe64d25..bcb89c6e9a 100644 --- a/src/syscall/syscall_linux_riscv64.go +++ b/src/syscall/syscall_linux_riscv64.go @@ -149,30 +149,6 @@ func utimes(path string, tv *[2]Timeval) (err error) { return utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0) } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - func (r *PtraceRegs) PC() uint64 { return r.Pc } func (r *PtraceRegs) SetPC(pc uint64) { r.Pc = pc } diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go index 0f6f6277bb..123664f5b2 100644 --- a/src/syscall/syscall_linux_s390x.go +++ b/src/syscall/syscall_linux_s390x.go @@ -74,30 +74,6 @@ func setTimeval(sec, usec int64) Timeval { return Timeval{Sec: sec, Usec: usec} } -func Pipe(p []int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, 0) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - -//sysnb pipe2(p *[2]_C_int, flags int) (err error) - -func Pipe2(p []int, flags int) (err error) { - if len(p) != 2 { - return EINVAL - } - var pp [2]_C_int - err = pipe2(&pp, flags) - p[0] = int(pp[0]) - p[1] = int(pp[1]) - return -} - // Linux on s390x uses the old mmap interface, which requires arguments to be passed in a struct. // mmap2 also requires arguments to be passed in a struct; it is currently not exposed in . func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) { diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go index ac822d6f7a..a1394d32cb 100644 --- a/src/syscall/zsyscall_linux_386.go +++ b/src/syscall/zsyscall_linux_386.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1048,26 +1058,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Dup2(oldfd int, newfd int) (err error) { _, _, e1 := Syscall(SYS_DUP2, uintptr(oldfd), uintptr(newfd), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go index ed37fa8dec..de6047d11c 100644 --- a/src/syscall/zsyscall_linux_amd64.go +++ b/src/syscall/zsyscall_linux_amd64.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1644,23 +1654,3 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go index 213aaf3bac..3663b2eead 100644 --- a/src/syscall/zsyscall_linux_arm.go +++ b/src/syscall/zsyscall_linux_arm.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1048,26 +1058,6 @@ func Munlockall() (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe(p *[2]_C_int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) fd = int(r0) diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go index e2f9c0fd9b..19a9c0ca61 100644 --- a/src/syscall/zsyscall_linux_arm64.go +++ b/src/syscall/zsyscall_linux_arm64.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1555,16 +1565,6 @@ func Gettimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) { r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/src/syscall/zsyscall_linux_mips.go b/src/syscall/zsyscall_linux_mips.go index 617c2f5466..966b2e1f2c 100644 --- a/src/syscall/zsyscall_linux_mips.go +++ b/src/syscall/zsyscall_linux_mips.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1646,28 +1656,6 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe() (p1 int, p2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - p1 = int(r0) - p2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) { r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset)) xaddr = uintptr(r0) diff --git a/src/syscall/zsyscall_linux_mips64.go b/src/syscall/zsyscall_linux_mips64.go index 793d4b9884..c6812a0515 100644 --- a/src/syscall/zsyscall_linux_mips64.go +++ b/src/syscall/zsyscall_linux_mips64.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1642,16 +1652,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_mips64le.go b/src/syscall/zsyscall_linux_mips64le.go index 54e1760bda..eaaf7dfb41 100644 --- a/src/syscall/zsyscall_linux_mips64le.go +++ b/src/syscall/zsyscall_linux_mips64le.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1642,16 +1652,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_mipsle.go b/src/syscall/zsyscall_linux_mipsle.go index ba7e2118c0..bb159f1fe7 100644 --- a/src/syscall/zsyscall_linux_mipsle.go +++ b/src/syscall/zsyscall_linux_mipsle.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1646,28 +1656,6 @@ func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe() (p1 int, p2 int, err error) { - r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) - p1 = int(r0) - p2 = int(r1) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func mmap2(addr uintptr, length uintptr, prot int, flags int, fd int, pageOffset uintptr) (xaddr uintptr, err error) { r0, _, e1 := Syscall6(SYS_MMAP2, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(pageOffset)) xaddr = uintptr(r0) diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go index c3437722e0..8a4328560a 100644 --- a/src/syscall/zsyscall_linux_ppc64.go +++ b/src/syscall/zsyscall_linux_ppc64.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1703,16 +1713,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func syncFileRange2(fd int, flags int, off int64, n int64) (err error) { _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go index acc34a76d2..274b55ce6b 100644 --- a/src/syscall/zsyscall_linux_ppc64le.go +++ b/src/syscall/zsyscall_linux_ppc64le.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1703,16 +1713,6 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func syncFileRange2(fd int, flags int, off int64, n int64) (err error) { _, _, e1 := Syscall6(SYS_SYNC_FILE_RANGE2, uintptr(fd), uintptr(flags), uintptr(off), uintptr(n), 0, 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_riscv64.go b/src/syscall/zsyscall_linux_riscv64.go index d662d780db..e21dc46f32 100644 --- a/src/syscall/zsyscall_linux_riscv64.go +++ b/src/syscall/zsyscall_linux_riscv64.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1555,16 +1565,6 @@ func Gettimeofday(tv *Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func ppoll(fds *pollFd, nfds int, timeout *Timespec, sigmask *sigset_t) (n int, err error) { r0, _, e1 := Syscall6(SYS_PPOLL, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/src/syscall/zsyscall_linux_s390x.go b/src/syscall/zsyscall_linux_s390x.go index 20f8c61366..fc667e7c3f 100644 --- a/src/syscall/zsyscall_linux_s390x.go +++ b/src/syscall/zsyscall_linux_s390x.go @@ -76,6 +76,16 @@ func openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pipe2(p *[2]_C_int, flags int) (err error) { + _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func readlinkat(dirfd int, path string, buf []byte) (n int, err error) { var _p0 *byte _p0, err = BytePtrFromString(path) @@ -1490,13 +1500,3 @@ func utimes(path string, times *[2]Timeval) (err error) { } return } - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func pipe2(p *[2]_C_int, flags int) (err error) { - _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} -- cgit v1.2.3-54-g00ecf From 3fa7dbeff53b56edd98f295bd0c34423c080ac57 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 17 Sep 2021 22:34:15 +0200 Subject: cmd/go: fix GOARCH value in GOAMD64 docs GOAMD64 is for GOARCH=amd64. Fix the GOAMD64 environment variable docs introduced by CL 349595. Change-Id: I794990ebe2e306d21ed275446fc52373bfe4ae7d Reviewed-on: https://go-review.googlesource.com/c/go/+/350534 Trust: Tobias Klauser Run-TryBot: Tobias Klauser Reviewed-by: Ian Lance Taylor Reviewed-by: Matthew Dempsky TryBot-Result: Go Bot --- src/cmd/go/alldocs.go | 2 +- src/cmd/go/internal/help/helpdoc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 35c60744b8..9753ebba3e 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1988,7 +1988,7 @@ // For GOARCH=386, how to implement floating point instructions. // Valid values are sse2 (default), softfloat. // GOAMD64 -// For GOARCH=GOAMD64, the microarchitecture level for which to compile. +// For GOARCH=amd64, the microarchitecture level for which to compile. // Valid values are v1 (default), v2, v3, v4. // See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels. // GOMIPS diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 91876cefe0..749dcf192b 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -593,7 +593,7 @@ Architecture-specific environment variables: For GOARCH=386, how to implement floating point instructions. Valid values are sse2 (default), softfloat. GOAMD64 - For GOARCH=GOAMD64, the microarchitecture level for which to compile. + For GOARCH=amd64, the microarchitecture level for which to compile. Valid values are v1 (default), v2, v3, v4. See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels. GOMIPS -- cgit v1.2.3-54-g00ecf From 3fa35b5f9741d7d1c9a9e047057c7210da04fbba Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 15 Sep 2021 17:18:37 -0400 Subject: go/types: ensure that we always get a new signature in expandNamed CL 349412 introduced a bug when Checker.subst does not return a new signature: we were still setting the receiver to the instantiated type. I'm not sure how this could manifest in practice (other than confusing object strings). It's possible that I could generate a testdata-driven test for this, but in the interest of time I just added a test to verify object strings. Change-Id: I29bc8e1419ddc4574755c3def52d18cb71c738eb Reviewed-on: https://go-review.googlesource.com/c/go/+/350143 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/instantiate_test.go | 43 ++++++++++++++++++++++++++++++++++++++++ src/go/types/named.go | 6 ++++++ 2 files changed, 49 insertions(+) diff --git a/src/go/types/instantiate_test.go b/src/go/types/instantiate_test.go index 851800e76d..0c66acb875 100644 --- a/src/go/types/instantiate_test.go +++ b/src/go/types/instantiate_test.go @@ -6,6 +6,7 @@ package types_test import ( . "go/types" + "strings" "testing" ) @@ -109,3 +110,45 @@ var X T[int] } } } + +func TestImmutableSignatures(t *testing.T) { + const src = genericPkg + `p + +type T[P any] struct{} + +func (T[P]) m() {} + +var _ T[int] +` + pkg, err := pkgFor(".", src, nil) + if err != nil { + t.Fatal(err) + } + typ := pkg.Scope().Lookup("T").Type().(*Named) + obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m") + if obj == nil { + t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj) + } + + // Verify that the original method is not mutated by instantiating T (this + // bug manifested when subst did not return a new signature). + want := "func (T[P]).m()" + if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want { + t.Errorf("instantiated %q, want %q", got, want) + } +} + +// Copied from errors.go. +func stripAnnotations(s string) string { + var b strings.Builder + for _, r := range s { + // strip #'s and subscript digits + if r < '₀' || '₀'+10 <= r { // '₀' == U+2080 + b.WriteRune(r) + } + } + if b.Len() < len(s) { + return b.String() + } + return s +} diff --git a/src/go/types/named.go b/src/go/types/named.go index 00fde16445..4a263410fc 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -309,6 +309,12 @@ func (check *Checker) completeMethod(env *Environment, m *Func) { smap := makeSubstMap(origSig.RecvTypeParams().list(), rtyp.targs.list()) sig := check.subst(orig.pos, origSig, smap, env).(*Signature) + if sig == origSig { + // No substitution occurred, but we still need to create a copy to hold the + // instantiated receiver. + copy := *origSig + sig = © + } sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp) m.typ = sig -- cgit v1.2.3-54-g00ecf From 50e450826916c777a7d61edab52793ed77314f7a Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Fri, 17 Sep 2021 12:18:19 -0700 Subject: cmd/compile: fix import/export of Init and Def fields. Change so that the Init and Def fields of assignments and OSELREVC2 nodes are exported/imported properly. A quirk of iimport.go is that it automatically converts an ODCL node to an ODCL/OAS sequence (where the OAS is to just zero out the declared variable). Given that the Inits are properly fixed, o.stmt needs adjustment for the OSELRECV2 case to skip over the new OAS nodes that are inserted only on re-import. Change-Id: Ic38017efca4b7ca9b3952ffbbfca067380902b7a Reviewed-on: https://go-review.googlesource.com/c/go/+/350809 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky Trust: Dan Scales --- src/cmd/compile/internal/typecheck/iexport.go | 25 ++++++++++++++++++++++--- src/cmd/compile/internal/typecheck/iimport.go | 21 ++++++++++++++++++--- src/cmd/compile/internal/walk/order.go | 6 ++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index f001017a86..a9522c3887 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -1456,10 +1456,23 @@ func (w *exportWriter) node(n ir.Node) { } } -// Caution: stmt will emit more than one node for statement nodes n that have a non-empty -// n.Ninit and where n cannot have a natural init section (such as in "if", "for", etc.). +func isNonEmptyAssign(n ir.Node) bool { + switch n.Op() { + case ir.OAS: + if n.(*ir.AssignStmt).Y != nil { + return true + } + case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: + return true + } + return false +} + +// Caution: stmt will emit more than one node for statement nodes n that have a +// non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init +// section (such as in "if", "for", etc.). func (w *exportWriter) stmt(n ir.Node) { - if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) { + if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) { // can't use stmtList here since we don't want the final OEND for _, n := range n.Init() { w.stmt(n) @@ -1495,8 +1508,10 @@ func (w *exportWriter) stmt(n ir.Node) { if n.Y != nil { w.op(ir.OAS) w.pos(n.Pos()) + w.stmtList(n.Init()) w.expr(n.X) w.expr(n.Y) + w.bool(n.Def) } case ir.OASOP: @@ -1517,8 +1532,10 @@ func (w *exportWriter) stmt(n ir.Node) { w.op(ir.OAS2) } w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprList(n.Lhs) w.exprList(n.Rhs) + w.bool(n.Def) case ir.ORETURN: n := n.(*ir.ReturnStmt) @@ -2065,8 +2082,10 @@ func (w *exportWriter) expr(n ir.Node) { n := n.(*ir.AssignListStmt) w.op(ir.OSELRECV2) w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprList(n.Lhs) w.exprList(n.Rhs) + w.bool(n.Def) default: base.Fatalf("cannot export %v (%d) node\n"+ diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index b3a0eb8871..3b3c2a2e2a 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -1619,7 +1619,12 @@ func (r *importReader) node() ir.Node { // unreachable - never exported case ir.OAS: - return ir.NewAssignStmt(r.pos(), r.expr(), r.expr()) + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignStmt(pos, r.expr(), r.expr()) + n.SetInit(init) + n.Def = r.bool() + return n case ir.OASOP: n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil) @@ -1636,7 +1641,12 @@ func (r *importReader) node() ir.Node { // unreachable - mapped to case OAS2 by exporter goto error } - return ir.NewAssignListStmt(r.pos(), op, r.exprList(), r.exprList()) + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList()) + n.SetInit(init) + n.Def = r.bool() + return n case ir.ORETURN: return ir.NewReturnStmt(r.pos(), r.exprList()) @@ -1721,7 +1731,12 @@ func (r *importReader) node() ir.Node { return n case ir.OSELRECV2: - return ir.NewAssignListStmt(r.pos(), ir.OSELRECV2, r.exprList(), r.exprList()) + pos := r.pos() + init := r.stmtList() + n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList()) + n.SetInit(init) + n.Def = r.bool() + return n default: base.Fatalf("cannot import %v (%d) node\n"+ diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 4de8858f26..7ac1f75c8f 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -941,6 +941,12 @@ func (o *orderState) stmt(n ir.Node) { if colas { if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n { init = init[1:] + + // iimport may have added a default initialization assignment, + // due to how it handles ODCL statements. + if len(init) > 0 && init[0].Op() == ir.OAS && init[0].(*ir.AssignStmt).X == n { + init = init[1:] + } } dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name))) ncas.PtrInit().Append(dcl) -- cgit v1.2.3-54-g00ecf From c10b98022027ce584b0571359439fae41a721dd3 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Fri, 10 Sep 2021 22:05:55 -0400 Subject: cmd/compile: restore tail call for method wrappers For certain type of method wrappers we used to generate a tail call. That was disabled in CL 307234 when register ABI is used, because with the current IR it was difficult to generate a tail call with the arguments in the right places. The problem was that the IR does not contain a CALL-like node with arguments; instead, it contains an OAS node that adjusts the receiver, than an OTAILCALL node that just contains the target, but no argument (with the assumption that the OAS node will put the adjusted receiver in the right place). With register ABI, putting arguments in registers are done in SSA. The assignment (OAS) doesn't put the receiver in register. This CL changes the IR of a tail call to take an actual OCALL node. Specifically, a tail call is represented as OTAILCALL (OCALL target args...) This way, the call target and args are connected through the OCALL node. So the call can be analyzed in SSA and the args can be passed in the right places. (Alternatively, we could have OTAILCALL node directly take the target and the args, without the OCALL node. Using an OCALL node is convenient as there are existing code that processes OCALL nodes which do not need to be changed. Also, a tail call is similar to ORETURN (OCALL target args...), except it doesn't preserve the frame. I did the former but I'm open to change.) The SSA representation is similar. Previously, the IR lowers to a Store the receiver then a BlockRetJmp which jumps to the target (without putting the arg in register). Now we use a TailCall op, which takes the target and the args. The call expansion pass and the register allocator handles TailCall pretty much like a StaticCall, and it will do the right ABI analysis and put the args in the right places. (Args other than the receiver are already in the right places. For register args it generates no code for them. For stack args currently it generates a self copy. I'll work on optimize that out.) BlockRetJmp is still used, signaling it is a tail call. The actual call is made in the TailCall op so BlockRetJmp generates no code (we could use BlockExit if we like). This slightly reduces binary size: old new cmd/go 14003088 13953936 cmd/link 6275552 6271456 Change-Id: I2d16d8d419fe1f17554916d317427383e17e27f0 Reviewed-on: https://go-review.googlesource.com/c/go/+/350145 Trust: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky Reviewed-by: David Chase --- src/cmd/compile/internal/amd64/ssa.go | 21 ++-- src/cmd/compile/internal/arm/ssa.go | 10 +- src/cmd/compile/internal/arm64/ssa.go | 10 +- src/cmd/compile/internal/escape/stmt.go | 3 +- src/cmd/compile/internal/inline/inl.go | 3 + src/cmd/compile/internal/ir/fmt.go | 2 +- src/cmd/compile/internal/ir/node_gen.go | 6 +- src/cmd/compile/internal/ir/stmt.go | 9 +- src/cmd/compile/internal/mips/ssa.go | 9 +- src/cmd/compile/internal/mips64/ssa.go | 9 +- src/cmd/compile/internal/ppc64/ssa.go | 10 +- src/cmd/compile/internal/reflectdata/reflect.go | 15 +-- src/cmd/compile/internal/riscv64/ssa.go | 9 +- src/cmd/compile/internal/s390x/ssa.go | 10 +- src/cmd/compile/internal/ssa/check.go | 3 - src/cmd/compile/internal/ssa/expand_calls.go | 16 ++- src/cmd/compile/internal/ssa/gen/386.rules | 1 + src/cmd/compile/internal/ssa/gen/386Ops.go | 1 + src/cmd/compile/internal/ssa/gen/AMD64.rules | 1 + src/cmd/compile/internal/ssa/gen/AMD64Ops.go | 1 + src/cmd/compile/internal/ssa/gen/ARM.rules | 1 + src/cmd/compile/internal/ssa/gen/ARM64.rules | 1 + src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 1 + src/cmd/compile/internal/ssa/gen/ARMOps.go | 1 + src/cmd/compile/internal/ssa/gen/MIPS.rules | 1 + src/cmd/compile/internal/ssa/gen/MIPS64.rules | 1 + src/cmd/compile/internal/ssa/gen/MIPS64Ops.go | 1 + src/cmd/compile/internal/ssa/gen/MIPSOps.go | 1 + src/cmd/compile/internal/ssa/gen/PPC64.rules | 1 + src/cmd/compile/internal/ssa/gen/PPC64Ops.go | 1 + src/cmd/compile/internal/ssa/gen/RISCV64.rules | 1 + src/cmd/compile/internal/ssa/gen/RISCV64Ops.go | 1 + src/cmd/compile/internal/ssa/gen/S390X.rules | 1 + src/cmd/compile/internal/ssa/gen/S390XOps.go | 1 + src/cmd/compile/internal/ssa/gen/Wasm.rules | 1 + src/cmd/compile/internal/ssa/gen/WasmOps.go | 1 + src/cmd/compile/internal/ssa/gen/genericOps.go | 4 +- src/cmd/compile/internal/ssa/opGen.go | 124 ++++++++++++++++++++++++ src/cmd/compile/internal/ssa/rewrite386.go | 3 + src/cmd/compile/internal/ssa/rewriteAMD64.go | 3 + src/cmd/compile/internal/ssa/rewriteARM.go | 3 + src/cmd/compile/internal/ssa/rewriteARM64.go | 3 + src/cmd/compile/internal/ssa/rewriteMIPS.go | 3 + src/cmd/compile/internal/ssa/rewriteMIPS64.go | 3 + src/cmd/compile/internal/ssa/rewritePPC64.go | 3 + src/cmd/compile/internal/ssa/rewriteRISCV64.go | 3 + src/cmd/compile/internal/ssa/rewriteS390X.go | 3 + src/cmd/compile/internal/ssa/rewriteWasm.go | 3 + src/cmd/compile/internal/ssa/writebarrier.go | 2 +- src/cmd/compile/internal/ssagen/abi.go | 20 ++-- src/cmd/compile/internal/ssagen/ssa.go | 29 ++++-- src/cmd/compile/internal/typecheck/typecheck.go | 1 + src/cmd/compile/internal/walk/stmt.go | 8 ++ src/cmd/compile/internal/wasm/ssa.go | 13 +-- src/cmd/compile/internal/x86/ssa.go | 9 +- test/abi/method_wrapper.go | 35 +++++++ 56 files changed, 319 insertions(+), 121 deletions(-) create mode 100644 test/abi/method_wrapper.go diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index fc547ebba0..30131bd559 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -1008,7 +1008,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { } r := v.Reg() getgFromTLS(s, r) - case ssa.OpAMD64CALLstatic: + case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail: if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal { // zeroing X15 when entering ABIInternal from ABI0 if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9 @@ -1017,6 +1017,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { // set G register from TLS getgFromTLS(s, x86.REG_R14) } + if v.Op == ssa.OpAMD64CALLtail { + s.TailCall(v) + break + } s.Call(v) if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 { // zeroing X15 when entering ABIInternal from ABI0 @@ -1314,22 +1318,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - if s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal { - // zeroing X15 when entering ABIInternal from ABI0 - if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9 - opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15) - } - // set G register from TLS - getgFromTLS(s, x86.REG_R14) - } - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockAMD64EQF: s.CombJump(b, next, &eqfJumps) diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go index 4b083cec46..8aac80a22e 100644 --- a/src/cmd/compile/internal/arm/ssa.go +++ b/src/cmd/compile/internal/arm/ssa.go @@ -696,6 +696,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter: s.Call(v) + case ssa.OpARMCALLtail: + s.TailCall(v) case ssa.OpARMCALLudiv: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -936,17 +938,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - case ssa.BlockARMEQ, ssa.BlockARMNE, ssa.BlockARMLT, ssa.BlockARMGE, ssa.BlockARMLE, ssa.BlockARMGT, diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index b985246117..c5e7a08914 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -1046,6 +1046,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p4.To.SetTarget(p) case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter: s.Call(v) + case ssa.OpARM64CALLtail: + s.TailCall(v) case ssa.OpARM64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -1241,17 +1243,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - case ssa.BlockARM64EQ, ssa.BlockARM64NE, ssa.BlockARM64LT, ssa.BlockARM64GE, ssa.BlockARM64LE, ssa.BlockARM64GT, diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go index c71848b8a1..0afb5d64ef 100644 --- a/src/cmd/compile/internal/escape/stmt.go +++ b/src/cmd/compile/internal/escape/stmt.go @@ -180,7 +180,8 @@ func (e *escape) stmt(n ir.Node) { e.goDeferStmt(n) case ir.OTAILCALL: - // TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it. + n := n.(*ir.TailCallStmt) + e.call(nil, n.Call) } } diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 073373144d..04d751869b 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -544,6 +544,9 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No call := call.(*ir.CallExpr) call.NoInline = true } + case ir.OTAILCALL: + n := n.(*ir.TailCallStmt) + n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)? // TODO do them here (or earlier), // so escape analysis can avoid more heapmoves. diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go index a99cb5ed98..29505357cc 100644 --- a/src/cmd/compile/internal/ir/fmt.go +++ b/src/cmd/compile/internal/ir/fmt.go @@ -386,7 +386,7 @@ func stmtFmt(n Node, s fmt.State) { case OTAILCALL: n := n.(*TailCallStmt) - fmt.Fprintf(s, "tailcall %v", n.Target) + fmt.Fprintf(s, "tailcall %v", n.Call) case OINLMARK: n := n.(*InlineMarkStmt) diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go index aa41c03beb..44988880c8 100644 --- a/src/cmd/compile/internal/ir/node_gen.go +++ b/src/cmd/compile/internal/ir/node_gen.go @@ -1331,15 +1331,15 @@ func (n *TailCallStmt) doChildren(do func(Node) bool) bool { if doNodes(n.init, do) { return true } - if n.Target != nil && do(n.Target) { + if n.Call != nil && do(n.Call) { return true } return false } func (n *TailCallStmt) editChildren(edit func(Node) Node) { editNodes(n.init, edit) - if n.Target != nil { - n.Target = edit(n.Target).(*Name) + if n.Call != nil { + n.Call = edit(n.Call).(*CallExpr) } } diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 69a74b9fdd..3482d7972e 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -385,14 +385,11 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt { // code generation to jump directly to another function entirely. type TailCallStmt struct { miniStmt - Target *Name + Call *CallExpr // the underlying call } -func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt { - if target.Op() != ONAME || target.Class != PFUNC { - base.FatalfAt(pos, "tail call to non-func %v", target) - } - n := &TailCallStmt{Target: target} +func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt { + n := &TailCallStmt{Call: call} n.pos = pos n.op = OTAILCALL return n diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go index e0447f38cb..6326f966bf 100644 --- a/src/cmd/compile/internal/mips/ssa.go +++ b/src/cmd/compile/internal/mips/ssa.go @@ -475,6 +475,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p6.To.SetTarget(p2) case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter: s.Call(v) + case ssa.OpMIPSCALLtail: + s.TailCall(v) case ssa.OpMIPSLoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -841,14 +843,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockMIPSEQ, ssa.BlockMIPSNE, ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ, ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ, diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go index e821a00876..990b9788f7 100644 --- a/src/cmd/compile/internal/mips64/ssa.go +++ b/src/cmd/compile/internal/mips64/ssa.go @@ -491,6 +491,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p6.To.SetTarget(p2) case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter: s.Call(v) + case ssa.OpMIPS64CALLtail: + s.TailCall(v) case ssa.OpMIPS64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -808,14 +810,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE, ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ, ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ, diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go index 11226f65a0..e366e06949 100644 --- a/src/cmd/compile/internal/ppc64/ssa.go +++ b/src/cmd/compile/internal/ppc64/ssa.go @@ -1829,6 +1829,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpPPC64CALLstatic: s.Call(v) + case ssa.OpPPC64CALLtail: + s.TailCall(v) + case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter: p := s.Prog(ppc64.AMOVD) p.From.Type = obj.TYPE_REG @@ -1980,14 +1983,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.AJMP) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockPPC64EQ, ssa.BlockPPC64NE, ssa.BlockPPC64LT, ssa.BlockPPC64GE, diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index f42bb338d0..6dbe3cb455 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -7,7 +7,6 @@ package reflectdata import ( "encoding/binary" "fmt" - "internal/buildcfg" "os" "sort" "strings" @@ -1869,15 +1868,11 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy // Disable tailcall for RegabiArgs for now. The IR does not connect the // arguments with the OTAILCALL node, and the arguments are not marshaled // correctly. - if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs && !generic { - // generate tail call: adjust pointer receiver and jump to embedded method. - left := dot.X // skip final .M - if !left.Type().IsPtr() { - left = typecheck.NodAddr(left) - } - as := ir.NewAssignStmt(base.Pos, nthis, typecheck.ConvNop(left, rcvr)) - fn.Body.Append(as) - fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name))) + if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic { + call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil) + call.Args = ir.ParamNames(tfn.Type()) + call.IsDDD = tfn.Type().IsVariadic() + fn.Body.Append(ir.NewTailCallStmt(base.Pos, call)) } else { fn.SetWrapper(true) // ignore frame for panic+recover matching var call *ir.CallExpr diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go index e400ca1ffe..1359b6a0c3 100644 --- a/src/cmd/compile/internal/riscv64/ssa.go +++ b/src/cmd/compile/internal/riscv64/ssa.go @@ -413,6 +413,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter: s.Call(v) + case ssa.OpRISCV64CALLtail: + s.TailCall(v) case ssa.OpRISCV64LoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -725,14 +727,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU: diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go index ddc05b36ad..deb6c79006 100644 --- a/src/cmd/compile/internal/s390x/ssa.go +++ b/src/cmd/compile/internal/s390x/ssa.go @@ -556,6 +556,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To.Reg = v.Reg() case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter: s.Call(v) + case ssa.OpS390XCALLtail: + s.TailCall(v) case ssa.OpS390XLoweredWB: p := s.Prog(obj.ACALL) p.To.Type = obj.TYPE_MEM @@ -899,17 +901,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { s.Br(s390x.ABR, b.Succs[0].Block()) } return - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: return case ssa.BlockRet: s.Prog(obj.ARET) return - case ssa.BlockRetJmp: - p := s.Prog(s390x.ABR) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - return } // Handle s390x-specific blocks. These blocks all have a diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 969fd96dbf..28edfd2237 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -66,9 +66,6 @@ func checkFunc(f *Func) { if !b.Controls[0].Type.IsMemory() { f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString()) } - if b.Aux == nil { - f.Fatalf("retjmp block %s has nil Aux field", b) - } case BlockPlain: if len(b.Succs) != 1 { f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 12c7b16acd..79434f33d3 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -1082,6 +1082,12 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { mem := m0 newArgs := []*Value{} oldArgs := []*Value{} + sp := x.sp + if v.Op == OpTailLECall { + // For tail call, we unwind the frame before the call so we'll use the caller's + // SP. + sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr) + } for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg. oldArgs = append(oldArgs, a) auxI := int64(i) @@ -1094,7 +1100,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { } // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move // TODO(register args) this will be more complicated with registers in the picture. - mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) + mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) } else { var rc registerCursor var result *[]*Value @@ -1107,7 +1113,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { if x.debug > 1 { x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) } - rc.init(aRegs, aux.abiInfo, result, x.sp) + rc.init(aRegs, aux.abiInfo, result, sp) mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc) } } @@ -1207,7 +1213,7 @@ func expandCalls(f *Func) { for _, v := range b.Values { firstArg := 0 switch v.Op { - case OpStaticLECall: + case OpStaticLECall, OpTailLECall: case OpInterLECall: firstArg = 1 case OpClosureLECall: @@ -1525,6 +1531,10 @@ func expandCalls(f *Func) { v.Op = OpStaticCall rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) v.Type = types.NewResults(append(rts, types.TypeMem)) + case OpTailLECall: + v.Op = OpTailCall + rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) + v.Type = types.NewResults(append(rts, types.TypeMem)) case OpClosureLECall: v.Op = OpClosureCall rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams()) diff --git a/src/cmd/compile/internal/ssa/gen/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules index 199b73c42f..7bdebedafe 100644 --- a/src/cmd/compile/internal/ssa/gen/386.rules +++ b/src/cmd/compile/internal/ssa/gen/386.rules @@ -317,6 +317,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (IsNonNil p) => (SETNE (TESTL p p)) diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go index 91f33c8374..3512d60865 100644 --- a/src/cmd/compile/internal/ssa/gen/386Ops.go +++ b/src/cmd/compile/internal/ssa/gen/386Ops.go @@ -455,6 +455,7 @@ func init() { }, {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules index 5b127c98e7..bfed3bc7fd 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules @@ -408,6 +408,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Lowering conditional moves // If the condition is a SETxx, we can just run a CMOV from the comparison that was diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go index 52ea7ac5e0..51cbf5f78a 100644 --- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go @@ -765,6 +765,7 @@ func init() { // With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific in and out registers. {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules index bcacbafe3a..bfb97e5271 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -351,6 +351,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // checks (NilCheck ...) => (LoweredNilCheck ...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index d99487c31b..7f9d5ec2bd 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -503,6 +503,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // checks (NilCheck ...) => (LoweredNilCheck ...) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index acfb2880c2..41231fb809 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -484,6 +484,7 @@ func init() { // function calls {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index d1f86039a3..eea0703f2c 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -431,6 +431,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules index 4ac9668ea9..639dda4b07 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS.rules +++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules @@ -334,6 +334,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // atomic intrinsics (AtomicLoad(8|32) ...) => (LoweredAtomicLoad(8|32) ...) diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64.rules b/src/cmd/compile/internal/ssa/gen/MIPS64.rules index fd04a6c3a8..292ff2fc79 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64.rules +++ b/src/cmd/compile/internal/ssa/gen/MIPS64.rules @@ -379,6 +379,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // atomic intrinsics (AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...) diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go index a18cd4289d..54c0741efd 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go @@ -276,6 +276,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go index 8177c7e2d1..5f73e9f2dc 100644 --- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go +++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go @@ -258,6 +258,7 @@ func init() { // function calls {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules index ce4b324b5e..411bb8d29d 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64.rules +++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules @@ -670,6 +670,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (GetClosurePtr ...) => (LoweredGetClosurePtr ...) diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go index d7d8a33a0a..a14d9cd490 100644 --- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go @@ -429,6 +429,7 @@ func init() { {name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true}, {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules index aa7c452d05..4290d1b85c 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules @@ -546,6 +546,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Atomic Intrinsics (AtomicLoad8 ...) => (LoweredAtomicLoad8 ...) diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go index ac1bcd2a06..8a3fdf75f7 100644 --- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go @@ -241,6 +241,7 @@ func init() { // Calls {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: call, aux: "CallOff", call: true}, // tail call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules index 88762f7045..b3928c6a1e 100644 --- a/src/cmd/compile/internal/ssa/gen/S390X.rules +++ b/src/cmd/compile/internal/ssa/gen/S390X.rules @@ -434,6 +434,7 @@ (StaticCall ...) => (CALLstatic ...) (ClosureCall ...) => (CALLclosure ...) (InterCall ...) => (CALLinter ...) +(TailCall ...) => (CALLtail ...) // Miscellaneous (IsNonNil p) => (LOCGR {s390x.NotEqual} (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0])) diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go index 00fce8e0e5..9b6ac2bfb6 100644 --- a/src/cmd/compile/internal/ssa/gen/S390XOps.go +++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go @@ -480,6 +480,7 @@ func init() { {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, {name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/Wasm.rules b/src/cmd/compile/internal/ssa/gen/Wasm.rules index 7ad3d1c72e..9e683b116c 100644 --- a/src/cmd/compile/internal/ssa/gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/gen/Wasm.rules @@ -307,6 +307,7 @@ (StaticCall ...) => (LoweredStaticCall ...) (ClosureCall ...) => (LoweredClosureCall ...) (InterCall ...) => (LoweredInterCall ...) +(TailCall ...) => (LoweredTailCall ...) // Miscellaneous (Convert ...) => (LoweredConvert ...) diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go index 7f7ae5e837..0d7327109a 100644 --- a/src/cmd/compile/internal/ssa/gen/WasmOps.go +++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go @@ -124,6 +124,7 @@ func init() { var WasmOps = []opData{ {name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem + {name: "LoweredTailCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index c183aedf2d..a0166f58f4 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -417,10 +417,12 @@ var genericOps = []opData{ {name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "StaticCall", argLength: -1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "InterCall", argLength: -1, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1..argN-1 are register inputs, argN=memory, auxint=arg size. Returns Result of register results, plus memory. + {name: "TailCall", argLength: -1, aux: "CallOff", call: true}, // tail call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory. {name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. {name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. + {name: "TailLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static tail call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem. // Conversions: signed extensions, zero (unsigned) extensions, truncations {name: "SignExt8to16", argLength: 1, typ: "Int16"}, @@ -638,7 +640,7 @@ var genericBlocks = []blockData{ {name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1] {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type) {name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result - {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is memory result, jumps to b.Aux.(*gc.Sym) + {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call {name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic // transient block state used for dead code removal diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 1ca99c1ba9..4ec13a6629 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -515,6 +515,7 @@ const ( Op386DUFFZERO Op386REPSTOSL Op386CALLstatic + Op386CALLtail Op386CALLclosure Op386CALLinter Op386DUFFCOPY @@ -993,6 +994,7 @@ const ( OpAMD64DUFFZERO OpAMD64REPSTOSQ OpAMD64CALLstatic + OpAMD64CALLtail OpAMD64CALLclosure OpAMD64CALLinter OpAMD64DUFFCOPY @@ -1269,6 +1271,7 @@ const ( OpARMCMOVWLSconst OpARMSRAcond OpARMCALLstatic + OpARMCALLtail OpARMCALLclosure OpARMCALLinter OpARMLoweredNilCheck @@ -1552,6 +1555,7 @@ const ( OpARM64CSNEG OpARM64CSETM OpARM64CALLstatic + OpARM64CALLtail OpARM64CALLclosure OpARM64CALLinter OpARM64LoweredNilCheck @@ -1697,6 +1701,7 @@ const ( OpMIPSMOVFD OpMIPSMOVDF OpMIPSCALLstatic + OpMIPSCALLtail OpMIPSCALLclosure OpMIPSCALLinter OpMIPSLoweredAtomicLoad8 @@ -1813,6 +1818,7 @@ const ( OpMIPS64MOVFD OpMIPS64MOVDF OpMIPS64CALLstatic + OpMIPS64CALLtail OpMIPS64CALLclosure OpMIPS64CALLinter OpMIPS64DUFFZERO @@ -2025,6 +2031,7 @@ const ( OpPPC64LoweredRound32F OpPPC64LoweredRound64F OpPPC64CALLstatic + OpPPC64CALLtail OpPPC64CALLclosure OpPPC64CALLinter OpPPC64LoweredZero @@ -2128,6 +2135,7 @@ const ( OpRISCV64SLTIU OpRISCV64MOVconvert OpRISCV64CALLstatic + OpRISCV64CALLtail OpRISCV64CALLclosure OpRISCV64CALLinter OpRISCV64DUFFZERO @@ -2386,6 +2394,7 @@ const ( OpS390XMOVDstoreconst OpS390XCLEAR OpS390XCALLstatic + OpS390XCALLtail OpS390XCALLclosure OpS390XCALLinter OpS390XInvertFlags @@ -2439,6 +2448,7 @@ const ( OpS390XLoweredZero OpWasmLoweredStaticCall + OpWasmLoweredTailCall OpWasmLoweredClosureCall OpWasmLoweredInterCall OpWasmLoweredAddr @@ -2785,9 +2795,11 @@ const ( OpClosureCall OpStaticCall OpInterCall + OpTailCall OpClosureLECall OpStaticLECall OpInterLECall + OpTailLECall OpSignExt8to16 OpSignExt8to32 OpSignExt8to64 @@ -5906,6 +5918,16 @@ var opcodeTable = [...]opInfo{ clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -13103,6 +13125,16 @@ var opcodeTable = [...]opInfo{ clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -16939,6 +16971,16 @@ var opcodeTable = [...]opInfo{ clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -20706,6 +20748,16 @@ var opcodeTable = [...]opInfo{ clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: -1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -22639,6 +22691,16 @@ var opcodeTable = [...]opInfo{ clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -24198,6 +24260,16 @@ var opcodeTable = [...]opInfo{ clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -27026,6 +27098,16 @@ var opcodeTable = [...]opInfo{ clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -28432,6 +28514,15 @@ var opcodeTable = [...]opInfo{ clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + call: true, + reg: regInfo{ + clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -32187,6 +32278,16 @@ var opcodeTable = [...]opInfo{ clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 }, }, + { + name: "CALLtail", + auxType: auxCallOff, + argLen: 1, + clobberFlags: true, + call: true, + reg: regInfo{ + clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 + }, + }, { name: "CALLclosure", auxType: auxCallOff, @@ -32857,6 +32958,15 @@ var opcodeTable = [...]opInfo{ clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g }, }, + { + name: "LoweredTailCall", + auxType: auxCallOff, + argLen: 1, + call: true, + reg: regInfo{ + clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g + }, + }, { name: "LoweredClosureCall", auxType: auxCallOff, @@ -35633,6 +35743,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "TailCall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "ClosureLECall", auxType: auxCallOff, @@ -35654,6 +35771,13 @@ var opcodeTable = [...]opInfo{ call: true, generic: true, }, + { + name: "TailLECall", + auxType: auxCallOff, + argLen: -1, + call: true, + generic: true, + }, { name: "SignExt8to16", argLen: 1, diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go index 1ec2d26f75..34f37867cf 100644 --- a/src/cmd/compile/internal/ssa/rewrite386.go +++ b/src/cmd/compile/internal/ssa/rewrite386.go @@ -652,6 +652,9 @@ func rewriteValue386(v *Value) bool { case OpSubPtr: v.Op = Op386SUBL return true + case OpTailCall: + v.Op = Op386CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index aa9293e347..e20161c920 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -1103,6 +1103,9 @@ func rewriteValueAMD64(v *Value) bool { case OpSubPtr: v.Op = OpAMD64SUBQ return true + case OpTailCall: + v.Op = OpAMD64CALLtail + return true case OpTrunc: return rewriteValueAMD64_OpTrunc(v) case OpTrunc16to8: diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index febb5566e3..3d2f862705 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -855,6 +855,9 @@ func rewriteValueARM(v *Value) bool { case OpSubPtr: v.Op = OpARMSUB return true + case OpTailCall: + v.Op = OpARMCALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 83dd771436..3df84e161a 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -1042,6 +1042,9 @@ func rewriteValueARM64(v *Value) bool { case OpSubPtr: v.Op = OpARM64SUB return true + case OpTailCall: + v.Op = OpARM64CALLtail + return true case OpTrunc: v.Op = OpARM64FRINTZD return true diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go index 429369d631..811ea9d9d3 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go @@ -544,6 +544,9 @@ func rewriteValueMIPS(v *Value) bool { case OpSubPtr: v.Op = OpMIPSSUB return true + case OpTailCall: + v.Op = OpMIPSCALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go index 772d7b66ef..1fbd556b5c 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go @@ -625,6 +625,9 @@ func rewriteValueMIPS64(v *Value) bool { case OpSubPtr: v.Op = OpMIPS64SUBV return true + case OpTailCall: + v.Op = OpMIPS64CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go index 96dee0bd21..b35331a624 100644 --- a/src/cmd/compile/internal/ssa/rewritePPC64.go +++ b/src/cmd/compile/internal/ssa/rewritePPC64.go @@ -772,6 +772,9 @@ func rewriteValuePPC64(v *Value) bool { case OpSubPtr: v.Op = OpPPC64SUB return true + case OpTailCall: + v.Op = OpPPC64CALLtail + return true case OpTrunc: v.Op = OpPPC64FTRUNC return true diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index 3a277ca369..f856a26d49 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -639,6 +639,9 @@ func rewriteValueRISCV64(v *Value) bool { case OpSubPtr: v.Op = OpRISCV64SUB return true + case OpTailCall: + v.Op = OpRISCV64CALLtail + return true case OpTrunc16to8: v.Op = OpCopy return true diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go index 8b41d62c31..0d63586149 100644 --- a/src/cmd/compile/internal/ssa/rewriteS390X.go +++ b/src/cmd/compile/internal/ssa/rewriteS390X.go @@ -819,6 +819,9 @@ func rewriteValueS390X(v *Value) bool { case OpSubPtr: v.Op = OpS390XSUB return true + case OpTailCall: + v.Op = OpS390XCALLtail + return true case OpTrunc: return rewriteValueS390X_OpTrunc(v) case OpTrunc16to8: diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go index 5dab09f85b..defd40ddd1 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -556,6 +556,9 @@ func rewriteValueWasm(v *Value) bool { case OpSubPtr: v.Op = OpWasmI64Sub return true + case OpTailCall: + v.Op = OpWasmLoweredTailCall + return true case OpTrunc: v.Op = OpWasmF64Trunc return true diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index d7510965f6..52f060b601 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -544,7 +544,7 @@ func IsStackAddr(v *Value) bool { v = v.Args[0] } switch v.Op { - case OpSP, OpLocalAddr, OpSelectNAddr: + case OpSP, OpLocalAddr, OpSelectNAddr, OpGetCallerSP: return true } return false diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go index 6d8c53e722..c54a734c75 100644 --- a/src/cmd/compile/internal/ssagen/abi.go +++ b/src/cmd/compile/internal/ssagen/abi.go @@ -382,18 +382,16 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) { } var tail ir.Node + call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil) + call.Args = ir.ParamNames(tfn.Type()) + call.IsDDD = tfn.Type().IsVariadic() + tail = call if tailcall { - tail = ir.NewTailCallStmt(base.Pos, f.Nname) - } else { - call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil) - call.Args = ir.ParamNames(tfn.Type()) - call.IsDDD = tfn.Type().IsVariadic() - tail = call - if tfn.Type().NumResults() > 0 { - n := ir.NewReturnStmt(base.Pos, nil) - n.Results = []ir.Node{call} - tail = n - } + tail = ir.NewTailCallStmt(base.Pos, call) + } else if tfn.Type().NumResults() > 0 { + n := ir.NewReturnStmt(base.Pos, nil) + n.Results = []ir.Node{call} + tail = n } fn.Body.Append(tail) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 1e7eda94fc..346a801508 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -1696,9 +1696,11 @@ func (s *state) stmt(n ir.Node) { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) - b := s.exit() - b.Kind = ssa.BlockRetJmp // override BlockRet - b.Aux = callTargetLSym(n.Target) + s.callResult(n.Call, callTail) + call := s.mem() + b := s.endBlock() + b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity. + b.SetControl(call) case ir.OCONTINUE, ir.OBREAK: n := n.(*ir.BranchStmt) @@ -3645,6 +3647,7 @@ const ( callDefer callDeferStack callGo + callTail ) type sfRtCallDef struct { @@ -4911,13 +4914,13 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val } } - if k != callNormal && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { + if k != callNormal && k != callTail && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { s.Fatalf("go/defer call with arguments: %v", n) } switch n.Op() { case ir.OCALLFUNC: - if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC { + if (k == callNormal || k == callTail) && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC { fn := fn.(*ir.Name) callee = fn if buildcfg.Experiment.RegabiArgs { @@ -4971,7 +4974,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val stksize := params.ArgWidth() // includes receiver, args, and results res := n.X.Type().Results() - if k == callNormal { + if k == callNormal || k == callTail { for _, p := range params.OutParams() { ACResults = append(ACResults, p.Type) } @@ -5018,7 +5021,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val // These are written in SP-offset order. argStart := base.Ctxt.FixedFrameSize() // Defer/go args. - if k != callNormal { + if k != callNormal && k != callTail { // Write closure (arg to newproc/deferproc). ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) // not argExtra callArgs = append(callArgs, closure) @@ -5068,6 +5071,10 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val case callee != nil: aux := ssa.StaticAuxCall(callTargetLSym(callee), params) call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + if k == callTail { + call.Op = ssa.OpTailLECall + stksize = 0 // Tail call does not use stack. We reuse caller's frame. + } default: s.Fatalf("bad call type %v %v", n.Op(), n) } @@ -7399,6 +7406,14 @@ func (s *State) Call(v *ssa.Value) *obj.Prog { return p } +// TailCall returns a new tail call instruction for the SSA value v. +// It is like Call, but for a tail call. +func (s *State) TailCall(v *ssa.Value) *obj.Prog { + p := s.Call(v) + p.As = obj.ARET + return p +} + // PrepareCall prepares to emit a CALL instruction for v and does call-related bookkeeping. // It must be called immediately before emitting the actual CALL instruction, // since it emits PCDATA for the stack map at the call (calls are safe points). diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index 404af5b1b2..42970f6a5e 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -879,6 +879,7 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) + n.Call = typecheck(n.Call, ctxStmt|ctxExpr).(*ir.CallExpr) return n case ir.OCHECKNIL: diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go index 4581bca3df..f09e916546 100644 --- a/src/cmd/compile/internal/walk/stmt.go +++ b/src/cmd/compile/internal/walk/stmt.go @@ -136,6 +136,14 @@ func walkStmt(n ir.Node) ir.Node { case ir.OTAILCALL: n := n.(*ir.TailCallStmt) + + var init ir.Nodes + n.Call.X = walkExpr(n.Call.X, &init) + + if len(init) > 0 { + init.Append(n) + return ir.NewBlockStmt(n.Pos(), init) + } return n case ir.OINLMARK: diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go index 0b2ca3fdbb..765051c944 100644 --- a/src/cmd/compile/internal/wasm/ssa.go +++ b/src/cmd/compile/internal/wasm/ssa.go @@ -88,13 +88,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.ARET) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) - - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockDefer: p := s.Prog(wasm.AGet) @@ -122,7 +116,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { func ssaGenValue(s *ssagen.State, v *ssa.Value) { switch v.Op { - case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall: + case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall, ssa.OpWasmLoweredTailCall: s.PrepareCall(v) if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn { // The runtime needs to inject jumps to @@ -141,6 +135,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p := s.Prog(obj.ACALL) p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym} p.Pos = v.Pos + if v.Op == ssa.OpWasmLoweredTailCall { + p.As = obj.ARET + } } else { getValue64(s, v.Args[0]) p := s.Prog(obj.ACALL) diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go index a06fdbcb71..32e29f347b 100644 --- a/src/cmd/compile/internal/x86/ssa.go +++ b/src/cmd/compile/internal/x86/ssa.go @@ -752,6 +752,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter: s.Call(v) + case ssa.Op386CALLtail: + s.TailCall(v) case ssa.Op386NEGL, ssa.Op386BSWAPL, ssa.Op386NOTL: @@ -892,14 +894,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { p.To.Type = obj.TYPE_BRANCH s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) } - case ssa.BlockExit: + case ssa.BlockExit, ssa.BlockRetJmp: case ssa.BlockRet: s.Prog(obj.ARET) - case ssa.BlockRetJmp: - p := s.Prog(obj.AJMP) - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = b.Aux.(*obj.LSym) case ssa.Block386EQF: s.CombJump(b, next, &eqfJumps) diff --git a/test/abi/method_wrapper.go b/test/abi/method_wrapper.go new file mode 100644 index 0000000000..7aa262fb52 --- /dev/null +++ b/test/abi/method_wrapper.go @@ -0,0 +1,35 @@ +// run + +// Copyright 2021 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 main + +type S int + +type T struct { + a int + S +} + +//go:noinline +func (s *S) M(a int, x [2]int, b float64, y [2]float64) (S, int, [2]int, float64, [2]float64) { + return *s, a, x, b, y +} + +var s S = 42 +var t = &T{S: s} + +var fn = (*T).M // force a method wrapper + +func main() { + a := 123 + x := [2]int{456, 789} + b := 1.2 + y := [2]float64{3.4, 5.6} + s1, a1, x1, b1, y1 := fn(t, a, x, b, y) + if a1 != a || x1 != x || b1 != b || y1 != y || s1 != s { + panic("FAIL") + } +} -- cgit v1.2.3-54-g00ecf From 07b30a4f77cf89a283c45c338f0cfcb68e15aab1 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Thu, 2 Sep 2021 08:47:40 -0700 Subject: cmd/compile: delay transformAssign if lhs/rhs have typeparam This also requires that we sometimes delay transformSelect(), if the assignments in the Comm part of the select have not been transformed. Fixes #48137 Change-Id: I163aa1f999d1e63616280dca807561b12b2aa779 Reviewed-on: https://go-review.googlesource.com/c/go/+/347915 Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/stencil.go | 3 +++ src/cmd/compile/internal/noder/stmt.go | 20 ++++++++++++++++---- test/typeparam/issue48137.go | 25 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/typeparam/issue48137.go diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index e60383f4e0..e2525a8f7e 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -995,6 +995,9 @@ func (subst *subster) node(n ir.Node) ir.Node { case ir.OSEND: transformSend(m.(*ir.SendStmt)) + case ir.OSELECT: + transformSelect(m.(*ir.SelectStmt)) + } } diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index 7f608bb91f..aefd9fcdaa 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -84,13 +84,13 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { // to know the types of the left and right sides in various cases. delay := false for _, e := range lhs { - if e.Typecheck() == 3 { + if e.Type().HasTParam() || e.Typecheck() == 3 { delay = true break } } for _, e := range rhs { - if e.Typecheck() == 3 { + if e.Type().HasTParam() || e.Typecheck() == 3 { delay = true break } @@ -145,8 +145,20 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { return g.forStmt(stmt) case *syntax.SelectStmt: n := g.selectStmt(stmt) - transformSelect(n.(*ir.SelectStmt)) - n.SetTypecheck(1) + + delay := false + for _, ncase := range n.(*ir.SelectStmt).Cases { + if ncase.Comm != nil && ncase.Comm.Typecheck() == 3 { + delay = true + break + } + } + if delay { + n.SetTypecheck(3) + } else { + transformSelect(n.(*ir.SelectStmt)) + n.SetTypecheck(1) + } return n case *syntax.SwitchStmt: return g.switchStmt(stmt) diff --git a/test/typeparam/issue48137.go b/test/typeparam/issue48137.go new file mode 100644 index 0000000000..3dd7810482 --- /dev/null +++ b/test/typeparam/issue48137.go @@ -0,0 +1,25 @@ +// run -gcflags=-G=3 + +// Copyright 2021 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 main + +type Constraint[T any] interface { + ~func() T +} + +func Foo[T Constraint[T]]() T { + var t T + + t = func() T { + return t + } + return t +} + +func main() { + type Bar func() Bar + Foo[Bar]() +} -- cgit v1.2.3-54-g00ecf From ac7c34767dd710643f5f5fea1720a18cc392b7f8 Mon Sep 17 00:00:00 2001 From: HowJMay Date: Thu, 16 Sep 2021 18:22:06 +0000 Subject: time: support fractional timezone minutes in MarshalBinary If the time is in 'LMT' and has fractional minute, then `MarshalBinary()` and `UnmarshalBinary()` will encode/decode the time in `timeBinaryVersionV2` in which the fractional minute is at bit 15 and 16, and presented in seconds. Fixes #39616 Change-Id: Ib762fb5fa26f54b1a8377a5dde0b994dd5a1236a GitHub-Last-Rev: 455d7a2496ba67d4a82890b14d57000e1a8a1415 GitHub-Pull-Request: golang/go#40293 Reviewed-on: https://go-review.googlesource.com/c/go/+/243402 Run-TryBot: Ian Lance Taylor TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor Trust: Carlos Amedee --- src/time/time.go | 30 ++++++++++++++++++++++++------ src/time/time_test.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/time/time.go b/src/time/time.go index 1919ebbc2c..edf0c62610 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -1162,19 +1162,26 @@ func (t Time) UnixNano() int64 { return (t.unixSec())*1e9 + int64(t.nsec()) } -const timeBinaryVersion byte = 1 +const ( + timeBinaryVersionV1 byte = iota + 1 // For general situation + timeBinaryVersionV2 // For LMT only +) // MarshalBinary implements the encoding.BinaryMarshaler interface. func (t Time) MarshalBinary() ([]byte, error) { var offsetMin int16 // minutes east of UTC. -1 is UTC. + var offsetSec int8 + version := timeBinaryVersionV1 if t.Location() == UTC { offsetMin = -1 } else { _, offset := t.Zone() if offset%60 != 0 { - return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute") + version = timeBinaryVersionV2 + offsetSec = int8(offset % 60) } + offset /= 60 if offset < -32768 || offset == -1 || offset > 32767 { return nil, errors.New("Time.MarshalBinary: unexpected zone offset") @@ -1185,8 +1192,8 @@ func (t Time) MarshalBinary() ([]byte, error) { sec := t.sec() nsec := t.nsec() enc := []byte{ - timeBinaryVersion, // byte 0 : version - byte(sec >> 56), // bytes 1-8: seconds + version, // byte 0 : version + byte(sec >> 56), // bytes 1-8: seconds byte(sec >> 48), byte(sec >> 40), byte(sec >> 32), @@ -1201,6 +1208,9 @@ func (t Time) MarshalBinary() ([]byte, error) { byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes byte(offsetMin), } + if version == timeBinaryVersionV2 { + enc = append(enc, byte(offsetSec)) + } return enc, nil } @@ -1212,11 +1222,16 @@ func (t *Time) UnmarshalBinary(data []byte) error { return errors.New("Time.UnmarshalBinary: no data") } - if buf[0] != timeBinaryVersion { + version := buf[0] + if version != timeBinaryVersionV1 && version != timeBinaryVersionV2 { return errors.New("Time.UnmarshalBinary: unsupported version") } - if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 { + wantLen := /*version*/ 1 + /*sec*/ 8 + /*nsec*/ 4 + /*zone offset*/ 2 + if version == timeBinaryVersionV2 { + wantLen++ + } + if len(buf) != wantLen { return errors.New("Time.UnmarshalBinary: invalid length") } @@ -1229,6 +1244,9 @@ func (t *Time) UnmarshalBinary(data []byte) error { buf = buf[4:] offset := int(int16(buf[1])|int16(buf[0])<<8) * 60 + if version == timeBinaryVersionV2 { + offset += int(buf[2]) + } *t = Time{} t.wall = uint64(nsec) diff --git a/src/time/time_test.go b/src/time/time_test.go index cea5f2d3f5..e2fb897b6d 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -767,7 +767,6 @@ var notEncodableTimes = []struct { time Time want string }{ - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"}, {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"}, @@ -1437,6 +1436,37 @@ func TestMarshalBinaryZeroTime(t *testing.T) { } } +func TestMarshalBinaryVersion2(t *testing.T) { + t0, err := Parse(RFC3339, "1880-01-01T00:00:00Z") + if err != nil { + t.Errorf("Failed to parse time, error = %v", err) + } + loc, err := LoadLocation("US/Eastern") + if err != nil { + t.Errorf("Failed to load location, error = %v", err) + } + t1 := t0.In(loc) + b, err := t1.MarshalBinary() + if err != nil { + t.Errorf("Failed to Marshal, error = %v", err) + } + + t2 := Time{} + err = t2.UnmarshalBinary(b) + if err != nil { + t.Errorf("Failed to Unmarshal, error = %v", err) + } + + if !(t0.Equal(t1) && t1.Equal(t2)) { + if !t0.Equal(t1) { + t.Errorf("The result t1: %+v after Marshal is not matched original t0: %+v", t1, t0) + } + if !t1.Equal(t2) { + t.Errorf("The result t2: %+v after Unmarshal is not matched original t1: %+v", t2, t1) + } + } +} + // Issue 17720: Zero value of time.Month fails to print func TestZeroMonthString(t *testing.T) { if got, want := Month(0).String(), "%!Month(0)"; got != want { -- cgit v1.2.3-54-g00ecf From 163871feb177ca0d7c690052b4cdd17bd57fcc04 Mon Sep 17 00:00:00 2001 From: Riley Avron Date: Tue, 14 Sep 2021 18:45:53 +0000 Subject: time: re-add space-padded day of year to docs CL 320252 reworked the time docs, but accidentally deleted the format __2 from the sentence describing the three-character day of year component. Change-Id: I3f583733028657c2a677358a25e062ea81835ce8 GitHub-Last-Rev: 2fa98324191500fd6a37097a9712ae23cc509269 GitHub-Pull-Request: golang/go#48387 Reviewed-on: https://go-review.googlesource.com/c/go/+/349929 Reviewed-by: Ian Lance Taylor Trust: Carlos Amedee --- src/time/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time/format.go b/src/time/format.go index c2bffb8ff6..7373892b97 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -74,7 +74,7 @@ import "errors" // for compatibility with fixed-width Unix time formats. A leading zero represents // a zero-padded value. // -// The formats and 002 are space-padded and zero-padded +// The formats __2 and 002 are space-padded and zero-padded // three-character day of year; there is no unpadded day of year format. // // A comma or decimal point followed by one or more zeros represents -- cgit v1.2.3-54-g00ecf From f01721efb941edadc2e6a897e42919b0d68480e1 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Thu, 16 Sep 2021 13:55:45 -0400 Subject: cmd/compile: remove self copies in tail-call wrappers The previous CL re-enables tail calls for method wrappers. But with the changed IR and SSA representation, for stack arguments it generates self copies. This CL makes the compiler detect the self copies and remove them. Change-Id: I7252572a1a47834f28b6706e45906e2356408e02 Reviewed-on: https://go-review.googlesource.com/c/go/+/350349 Trust: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/ssa/expand_calls.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go index 79434f33d3..a0f0e653aa 100644 --- a/src/cmd/compile/internal/ssa/expand_calls.go +++ b/src/cmd/compile/internal/ssa/expand_calls.go @@ -1098,6 +1098,17 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { if a.MemoryArg() != m0 { x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString()) } + if v.Op == OpTailLECall { + // It's common for a tail call passing the same arguments (e.g. method wrapper), + // so this would be a self copy. Detect this and optimize it out. + a0 := a.Args[0] + if a0.Op == OpLocalAddr { + n := a0.Aux.(*ir.Name) + if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset { + continue + } + } + } // "Dereference" of addressed (probably not-SSA-eligible) value becomes Move // TODO(register args) this will be more complicated with registers in the picture. mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos) @@ -1110,6 +1121,14 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) { } else { aOffset = aux.OffsetOfArg(auxI) } + if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 { + // It's common for a tail call passing the same arguments (e.g. method wrapper), + // so this would be a self copy. Detect this and optimize it out. + n := a.Aux.(*ir.Name) + if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset { + continue + } + } if x.debug > 1 { x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset) } -- cgit v1.2.3-54-g00ecf From 4b654c0eeca65ffc6588ffd9c99387a7e48002c1 Mon Sep 17 00:00:00 2001 From: Cherry Mui Date: Fri, 17 Sep 2021 16:26:13 -0400 Subject: cmd/compile: SSA ".this" variable We used to not SSA ".this" variable, because in tail-call method wrappers it relies on updating ".this" in place in memory, and the tail call IR node and SSA op do not have an explicit use of ".this". It is no longer the case for the new tail call representation. Remove the restriction. Change-Id: I4e1ce8459adbb0d5a80c64f1ece982737bd95305 Reviewed-on: https://go-review.googlesource.com/c/go/+/350751 Trust: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Reviewed-by: David Chase --- src/cmd/compile/internal/ssagen/ssa.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 346a801508..08f36ce7be 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -5311,12 +5311,6 @@ func (s *state) canSSAName(name *ir.Name) bool { return false } } - if name.Class == ir.PPARAM && name.Sym() != nil && name.Sym().Name == ".this" { - // wrappers generated by genwrapper need to update - // the .this pointer in place. - // TODO: treat as a PPARAMOUT? - return false - } return true // TODO: try to make more variables SSAable? } -- cgit v1.2.3-54-g00ecf From c894b442d1e5e150ad33fa3ce13dbfab1c037b3a Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Tue, 14 Sep 2021 15:02:55 -0700 Subject: net/rpc: remove warnings on incompatible methods at registration When registering an RPC server, the type being registered may have additional methods that are not meant to be exposed as RPC endpoints. Remove the warnings net/rpc produces in this case. The functionality to report warnings is kept in the code with a compile-time constant that can be enabled for debugging. The documentation of net/rpc states that only methods satisfying a set of criteria will be made available, while other methods will be ignored. Fixes #19957 Change-Id: I5f8a148b4be1fdfffb2cd2029871193eaf24b751 Reviewed-on: https://go-review.googlesource.com/c/go/+/350009 Reviewed-by: Daniel Lublin Reviewed-by: Damien Neil Trust: Carlos Amedee --- src/net/rpc/server.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go index 074c5b9b0d..bfc19ac97c 100644 --- a/src/net/rpc/server.go +++ b/src/net/rpc/server.go @@ -231,6 +231,10 @@ func (server *Server) RegisterName(name string, rcvr interface{}) error { return server.register(rcvr, name, true) } +// logRegisterError specifies whether to log problems during method registration. +// To debug registration, recompile the package with this set to true. +const logRegisterError = false + func (server *Server) register(rcvr interface{}, name string, useName bool) error { s := new(service) s.typ = reflect.TypeOf(rcvr) @@ -252,7 +256,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro s.name = sname // Install the methods - s.method = suitableMethods(s.typ, true) + s.method = suitableMethods(s.typ, logRegisterError) if len(s.method) == 0 { str := "" @@ -274,9 +278,9 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro return nil } -// suitableMethods returns suitable Rpc methods of typ, it will report -// error using log if reportErr is true. -func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { +// suitableMethods returns suitable Rpc methods of typ. It will log +// errors if logErr is true. +func suitableMethods(typ reflect.Type, logErr bool) map[string]*methodType { methods := make(map[string]*methodType) for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m) @@ -288,7 +292,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { } // Method needs three ins: receiver, *args, *reply. if mtype.NumIn() != 3 { - if reportErr { + if logErr { log.Printf("rpc.Register: method %q has %d input parameters; needs exactly three\n", mname, mtype.NumIn()) } continue @@ -296,7 +300,7 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { // First arg need not be a pointer. argType := mtype.In(1) if !isExportedOrBuiltinType(argType) { - if reportErr { + if logErr { log.Printf("rpc.Register: argument type of method %q is not exported: %q\n", mname, argType) } continue @@ -304,28 +308,28 @@ func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { // Second arg must be a pointer. replyType := mtype.In(2) if replyType.Kind() != reflect.Ptr { - if reportErr { + if logErr { log.Printf("rpc.Register: reply type of method %q is not a pointer: %q\n", mname, replyType) } continue } // Reply type must be exported. if !isExportedOrBuiltinType(replyType) { - if reportErr { + if logErr { log.Printf("rpc.Register: reply type of method %q is not exported: %q\n", mname, replyType) } continue } // Method needs one out. if mtype.NumOut() != 1 { - if reportErr { + if logErr { log.Printf("rpc.Register: method %q has %d output parameters; needs exactly one\n", mname, mtype.NumOut()) } continue } // The return type of the method must be error. if returnType := mtype.Out(0); returnType != typeOfError { - if reportErr { + if logErr { log.Printf("rpc.Register: return type of method %q is %q, must be error\n", mname, returnType) } continue -- cgit v1.2.3-54-g00ecf From 771b8ea4f4c56b3e27351807ade7ef72c3a15750 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Sat, 18 Sep 2021 23:46:47 +0700 Subject: cmd/compile: fix missing markHiddenClosureDead in deadcode pass CL 342350 fixed panic with dead hidden closures, by marking discarded hidden closure as dead, and won't compile them. However, the fix is incomplete. In case the "if" or "else" block end with panic or return statement: if true { return } # All nodes starts from here are dead the dead nodes must be processed with markHiddenClosureDead, but they are not, causing the compiler crashes. This CL adds that missing part. Fixes #48459 Change-Id: Ibdd10a61fc6459d139bbf4a66b0893b523ac6b67 Reviewed-on: https://go-review.googlesource.com/c/go/+/350695 Trust: Cuong Manh Le Run-TryBot: Cuong Manh Le TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/deadcode/deadcode.go | 1 + test/fixedbugs/issue48459.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/fixedbugs/issue48459.go diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go index 3658c89912..65a48b6803 100644 --- a/src/cmd/compile/internal/deadcode/deadcode.go +++ b/src/cmd/compile/internal/deadcode/deadcode.go @@ -117,6 +117,7 @@ func stmts(nn *ir.Nodes) { } if cut { + ir.VisitList((*nn)[i+1:len(*nn)], markHiddenClosureDead) *nn = (*nn)[:i+1] break } diff --git a/test/fixedbugs/issue48459.go b/test/fixedbugs/issue48459.go new file mode 100644 index 0000000000..ceb7788ae4 --- /dev/null +++ b/test/fixedbugs/issue48459.go @@ -0,0 +1,17 @@ +// compile + +// Copyright 2021 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 main + +func main() { + if true { + return + } + + defer func() { + recover() + }() +} -- cgit v1.2.3-54-g00ecf From 83b36ffb108cc6e6cc3282b94c090f70100b5ef0 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Sep 2021 09:09:55 -0700 Subject: cmd/compile: implement constant rotates on arm64 Explicit constant rotates work, but constant arguments to bits.RotateLeft* needed the additional rule. Fixes #48465 Change-Id: Ia7544f21d0e7587b6b6506f72421459cd769aea6 Reviewed-on: https://go-review.googlesource.com/c/go/+/350909 Trust: Keith Randall Trust: Josh Bleecher Snyder Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/gen/ARM.rules | 3 ++- src/cmd/compile/internal/ssa/gen/ARM64.rules | 3 +++ src/cmd/compile/internal/ssa/rewriteARM.go | 33 ++++++++++++++--------- src/cmd/compile/internal/ssa/rewriteARM64.go | 40 ++++++++++++++++++++++++++++ test/codegen/rotate.go | 19 +++++++++++-- 5 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules index bfb97e5271..8a755b404b 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -508,6 +508,8 @@ (TST x (MOVWconst [c])) => (TSTconst [c] x) (TEQ x (MOVWconst [c])) => (TEQconst [c] x) +(SRR x (MOVWconst [c])) => (SRRconst x [c&31]) + // Canonicalize the order of arguments to comparisons - helps with CSE. (CMP x y) && canonLessThan(x,y) => (InvertFlags (CMP y x)) @@ -1136,7 +1138,6 @@ ( ORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x) (XORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x) -(RotateLeft32 x (MOVWconst [c])) => (SRRconst [-c&31] x) (RotateLeft16 x (MOVWconst [c])) => (Or16 (Lsh16x32 x (MOVWconst [c&15])) (Rsh16Ux32 x (MOVWconst [-c&15]))) (RotateLeft8 x (MOVWconst [c])) => (Or8 (Lsh8x32 x (MOVWconst [c&7])) (Rsh8Ux32 x (MOVWconst [-c&7]))) (RotateLeft32 x y) => (SRR x (RSBconst [0] y)) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index 7f9d5ec2bd..f63b2557c5 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1175,6 +1175,9 @@ (CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x) (CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x)) +(ROR x (MOVDconst [c])) => (RORconst x [c&63]) +(RORW x (MOVDconst [c])) => (RORWconst x [c&31]) + // Canonicalize the order of arguments to comparisons - helps with CSE. ((CMP|CMPW) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW) y x)) diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index 3d2f862705..6807507218 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -338,6 +338,8 @@ func rewriteValueARM(v *Value) bool { return rewriteValueARM_OpARMSRL(v) case OpARMSRLconst: return rewriteValueARM_OpARMSRLconst(v) + case OpARMSRR: + return rewriteValueARM_OpARMSRR(v) case OpARMSUB: return rewriteValueARM_OpARMSUB(v) case OpARMSUBD: @@ -10523,6 +10525,24 @@ func rewriteValueARM_OpARMSRLconst(v *Value) bool { } return false } +func rewriteValueARM_OpARMSRR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (SRR x (MOVWconst [c])) + // result: (SRRconst x [c&31]) + for { + x := v_0 + if v_1.Op != OpARMMOVWconst { + break + } + c := auxIntToInt32(v_1.AuxInt) + v.reset(OpARMSRRconst) + v.AuxInt = int32ToAuxInt(c & 31) + v.AddArg(x) + return true + } + return false +} func rewriteValueARM_OpARMSUB(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -14904,19 +14924,6 @@ func rewriteValueARM_OpRotateLeft32(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] b := v.Block - // match: (RotateLeft32 x (MOVWconst [c])) - // result: (SRRconst [-c&31] x) - for { - x := v_0 - if v_1.Op != OpARMMOVWconst { - break - } - c := auxIntToInt32(v_1.AuxInt) - v.reset(OpARMSRRconst) - v.AuxInt = int32ToAuxInt(-c & 31) - v.AddArg(x) - return true - } // match: (RotateLeft32 x y) // result: (SRR x (RSBconst [0] y)) for { diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 3df84e161a..2bce96f0b2 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -323,6 +323,10 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ORshiftRA(v) case OpARM64ORshiftRL: return rewriteValueARM64_OpARM64ORshiftRL(v) + case OpARM64ROR: + return rewriteValueARM64_OpARM64ROR(v) + case OpARM64RORW: + return rewriteValueARM64_OpARM64RORW(v) case OpARM64RORWconst: return rewriteValueARM64_OpARM64RORWconst(v) case OpARM64RORconst: @@ -19956,6 +19960,42 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64ROR(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ROR x (MOVDconst [c])) + // result: (RORconst x [c&63]) + for { + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64RORconst) + v.AuxInt = int64ToAuxInt(c & 63) + v.AddArg(x) + return true + } + return false +} +func rewriteValueARM64_OpARM64RORW(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (RORW x (MOVDconst [c])) + // result: (RORWconst x [c&31]) + for { + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64RORWconst) + v.AuxInt = int64ToAuxInt(c & 31) + v.AddArg(x) + return true + } + return false +} func rewriteValueARM64_OpARM64RORWconst(v *Value) bool { v_0 := v.Args[0] // match: (RORWconst [c] (RORWconst [d] x)) diff --git a/test/codegen/rotate.go b/test/codegen/rotate.go index 519cc83263..70489a2adc 100644 --- a/test/codegen/rotate.go +++ b/test/codegen/rotate.go @@ -34,8 +34,15 @@ func rot64(x uint64) uint64 { // ppc64le:"ROTL\t[$]9" a += x<<9 ^ x>>55 - // s390x:"RISBGZ\t[$]0, [$]63, [$]7, " + // amd64:"ROLQ\t[$]10" + // arm64:"ROR\t[$]54" + // s390x:"RISBGZ\t[$]0, [$]63, [$]10, " + // ppc64:"ROTL\t[$]10" + // ppc64le:"ROTL\t[$]10" // arm64:"ROR\t[$]57" // TODO this is not great line numbering, but then again, the instruction did appear + // s390x:"RISBGZ\t[$]0, [$]63, [$]7, " // TODO ditto + a += bits.RotateLeft64(x, 10) + return a } @@ -64,8 +71,16 @@ func rot32(x uint32) uint32 { // ppc64le:"ROTLW\t[$]9" a += x<<9 ^ x>>23 - // s390x:"RLL\t[$]7" + // amd64:"ROLL\t[$]10" + // arm:"MOVW\tR\\d+@>22" + // arm64:"RORW\t[$]22" + // s390x:"RLL\t[$]10" + // ppc64:"ROTLW\t[$]10" + // ppc64le:"ROTLW\t[$]10" // arm64:"RORW\t[$]25" // TODO this is not great line numbering, but then again, the instruction did appear + // s390x:"RLL\t[$]7" // TODO ditto + a += bits.RotateLeft32(x, 10) + return a } -- cgit v1.2.3-54-g00ecf From 315dbd10c90db3b41af2c975a5fbfa2ec7615c1b Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Sep 2021 09:23:37 -0700 Subject: cmd/compile: fold double negate on arm64 Fixes #48467 Change-Id: I52305dbf561ee3eee6c1f053e555a3a6ec1ab892 Reviewed-on: https://go-review.googlesource.com/c/go/+/350910 Trust: Keith Randall Trust: Josh Bleecher Snyder Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Josh Bleecher Snyder --- src/cmd/compile/internal/ssa/gen/ARM64.rules | 1 + src/cmd/compile/internal/ssa/rewriteARM64.go | 10 ++++++++++ test/codegen/bits.go | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index f63b2557c5..23ae3b1286 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1363,6 +1363,7 @@ (XOR x (MVN y)) => (EON x y) (OR x (MVN y)) => (ORN x y) (MVN (XOR x y)) => (EON x y) +(NEG (NEG x)) => x (CSEL [cc] (MOVDconst [-1]) (MOVDconst [0]) flag) => (CSETM [cc] flag) (CSEL [cc] (MOVDconst [0]) (MOVDconst [-1]) flag) => (CSETM [arm64Negate(cc)] flag) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 2bce96f0b2..661714307a 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -15691,6 +15691,16 @@ func rewriteValueARM64_OpARM64NEG(v *Value) bool { v.AddArg2(x, y) return true } + // match: (NEG (NEG x)) + // result: x + for { + if v_0.Op != OpARM64NEG { + break + } + x := v_0.Args[0] + v.copyOf(x) + return true + } // match: (NEG (MOVDconst [c])) // result: (MOVDconst [-c]) for { diff --git a/test/codegen/bits.go b/test/codegen/bits.go index 8117a62307..8e973d5726 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -6,6 +6,8 @@ package codegen +import "math/bits" + /************************************ * 64-bit instructions ************************************/ @@ -355,3 +357,9 @@ func issue44228b(a []int32, i int) bool { // amd64: "BTL", -"SHL" return a[i>>5]&(1<<(i&31)) != 0 } + +func issue48467(x, y uint64) uint64 { + // arm64: -"NEG" + d, borrow := bits.Sub64(x, y, 0) + return x - d&(-borrow) +} -- cgit v1.2.3-54-g00ecf From a83a5587331392fc9483d183e446586b463ad8aa Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sun, 19 Sep 2021 09:23:21 -0700 Subject: cmd/compile: fix export/import of range loop. As with other recent issues, the Init field of a range loop was not being handled properly. Generally, it is much better to explicitly import/export the Init statements, else they are incorrectly added before the associated node, rather than as the Init value of the node. This was causing labels to not be correctly added to the range loop that it is immediately preceding. Made the ORANGE handling completely similar to the OFOR handling. Fixes #48462 Change-Id: I999530e84f9357f81deaa3dda50660061f710e7c Reviewed-on: https://go-review.googlesource.com/c/go/+/350911 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Trust: Dan Scales --- src/cmd/compile/internal/typecheck/iexport.go | 3 ++- src/cmd/compile/internal/typecheck/iimport.go | 16 +++++++++------- test/typeparam/issue48462.dir/a.go | 22 ++++++++++++++++++++++ test/typeparam/issue48462.dir/main.go | 23 +++++++++++++++++++++++ test/typeparam/issue48462.go | 7 +++++++ 5 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 test/typeparam/issue48462.dir/a.go create mode 100644 test/typeparam/issue48462.dir/main.go create mode 100644 test/typeparam/issue48462.go diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index a9522c3887..def9408544 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -1472,7 +1472,7 @@ func isNonEmptyAssign(n ir.Node) bool { // non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init // section (such as in "if", "for", etc.). func (w *exportWriter) stmt(n ir.Node) { - if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) { + if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE { // can't use stmtList here since we don't want the final OEND for _, n := range n.Init() { w.stmt(n) @@ -1573,6 +1573,7 @@ func (w *exportWriter) stmt(n ir.Node) { n := n.(*ir.RangeStmt) w.op(ir.ORANGE) w.pos(n.Pos()) + w.stmtList(n.Init()) w.exprsOrNil(n.Key, n.Value) w.expr(n.X) w.stmtList(n.Body) diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index 3b3c2a2e2a..a7339903fc 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -1515,7 +1515,7 @@ func (r *importReader) node() ir.Node { if go117ExportTypes { n.SetOp(op) } - *n.PtrInit() = init + n.SetInit(init) n.IsDDD = r.bool() if go117ExportTypes { n.SetType(r.exoticType()) @@ -1660,26 +1660,28 @@ func (r *importReader) node() ir.Node { case ir.OIF: pos, init := r.pos(), r.stmtList() n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.OFOR: pos, init := r.pos(), r.stmtList() cond, post := r.exprsOrNil() n := ir.NewForStmt(pos, nil, cond, post, r.stmtList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.ORANGE: - pos := r.pos() + pos, init := r.pos(), r.stmtList() k, v := r.exprsOrNil() - return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList()) + n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList()) + n.SetInit(init) + return n case ir.OSELECT: pos := r.pos() init := r.stmtList() n := ir.NewSelectStmt(pos, r.commList()) - *n.PtrInit() = init + n.SetInit(init) return n case ir.OSWITCH: @@ -1687,7 +1689,7 @@ func (r *importReader) node() ir.Node { init := r.stmtList() x, _ := r.exprsOrNil() n := ir.NewSwitchStmt(pos, x, r.caseList(x)) - *n.PtrInit() = init + n.SetInit(init) return n // case OCASE: diff --git a/test/typeparam/issue48462.dir/a.go b/test/typeparam/issue48462.dir/a.go new file mode 100644 index 0000000000..26c704dbe4 --- /dev/null +++ b/test/typeparam/issue48462.dir/a.go @@ -0,0 +1,22 @@ +// Copyright 2021 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 a + +func Unique[T comparable](set []T) []T { + nset := make([]T, 0, 8) + +loop: + for _, s := range set { + for _, e := range nset { + if s == e { + continue loop + } + } + + nset = append(nset, s) + } + + return nset +} diff --git a/test/typeparam/issue48462.dir/main.go b/test/typeparam/issue48462.dir/main.go new file mode 100644 index 0000000000..8054ddd327 --- /dev/null +++ b/test/typeparam/issue48462.dir/main.go @@ -0,0 +1,23 @@ +// Copyright 2021 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 main + +import ( + "fmt" + "reflect" + + "a" +) + +func main() { + e := []int{1, 2, 2, 3, 1, 6} + + got := a.Unique(e) + want := []int{1, 2, 3, 6} + if !reflect.DeepEqual(got, want) { + panic(fmt.Sprintf("got %d, want %d", got, want)) + } + +} diff --git a/test/typeparam/issue48462.go b/test/typeparam/issue48462.go new file mode 100644 index 0000000000..76930e5e4f --- /dev/null +++ b/test/typeparam/issue48462.go @@ -0,0 +1,7 @@ +// rundir -G=3 + +// Copyright 2021 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 ignored -- cgit v1.2.3-54-g00ecf From 119213566a8e0da729a8bccc7d7f7d525f0c1cf9 Mon Sep 17 00:00:00 2001 From: jochen weber Date: Fri, 14 May 2021 12:45:13 +0000 Subject: cmd/cgo: remove hardcoded '-pie' ldflag for linux/arm a minimally invasive fix proposal for #45940. which keeps the fix for #26197. an alternative for (#26197) could be to fail if we have both flags. adding/removing a flag without an message to the user is inconvenient. Change-Id: I6ac2524d81ff57202fbe3032a53afd5106270a9e GitHub-Last-Rev: edaf02fa455329b5d794a139f99874b5e8cc12d1 GitHub-Pull-Request: golang/go#45989 Reviewed-on: https://go-review.googlesource.com/c/go/+/317569 Reviewed-by: Cherry Mui Run-TryBot: Cherry Mui TryBot-Result: Go Bot Trust: Bryan C. Mills --- src/cmd/go/internal/work/exec.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index f7fae9fdd9..f82028aef6 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -2963,18 +2963,24 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles)) dynobj := objdir + "_cgo_.o" - // we need to use -pie for Linux/ARM to get accurate imported sym ldflags := cgoLDFLAGS if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" { - // -static -pie doesn't make sense, and causes link errors. - // Issue 26197. - n := make([]string, 0, len(ldflags)) - for _, flag := range ldflags { - if flag != "-static" { - n = append(n, flag) + if !str.Contains(ldflags, "-no-pie") { + // we need to use -pie for Linux/ARM to get accurate imported sym (added in https://golang.org/cl/5989058) + // this seems to be outdated, but we don't want to break existing builds depending on this (Issue 45940) + ldflags = append(ldflags, "-pie") + } + if str.Contains(ldflags, "-pie") && str.Contains(ldflags, "-static") { + // -static -pie doesn't make sense, and causes link errors. + // Issue 26197. + n := make([]string, 0, len(ldflags)-1) + for _, flag := range ldflags { + if flag != "-static" { + n = append(n, flag) + } } + ldflags = n } - ldflags = append(n, "-pie") } if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil { return err -- cgit v1.2.3-54-g00ecf From a81b0dc6ee9096e3885e083e2dea2c743c0f4540 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sun, 19 Sep 2021 18:18:41 -0700 Subject: cmd/compile: rename instType -> instanceType This is to keep in alignment with the naming in go/types. Change-Id: I19ded29b39665d7b892fdbc8e92c7f15caf7ab66 Reviewed-on: https://go-review.googlesource.com/c/go/+/350950 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Robert Griesemer Trust: Dan Scales --- src/cmd/compile/internal/typecheck/iexport.go | 4 ++-- src/cmd/compile/internal/typecheck/iimport.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index def9408544..bbdad09782 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -255,7 +255,7 @@ const ( structType interfaceType typeParamType - instType + instanceType // Instantiation of a generic type unionType ) @@ -893,7 +893,7 @@ func (w *exportWriter) doTyp(t *types.Type) { if strings.Index(s.Name, "[") < 0 { base.Fatalf("incorrect name for instantiated type") } - w.startType(instType) + w.startType(instanceType) w.pos(t.Pos()) // Export the type arguments for the instantiated type. The // instantiated type could be in a method header (e.g. "func (v diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go index a7339903fc..ec4057a8d0 100644 --- a/src/cmd/compile/internal/typecheck/iimport.go +++ b/src/cmd/compile/internal/typecheck/iimport.go @@ -821,7 +821,7 @@ func (r *importReader) typ1() *types.Type { } return n.Type() - case instType: + case instanceType: if r.p.exportVersion < iexportVersionGenerics { base.Fatalf("unexpected instantiation type") } -- cgit v1.2.3-54-g00ecf From 2d9b4864201d76bd30b55a7d74d73a24cc3ae165 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sun, 19 Sep 2021 18:26:19 -0700 Subject: cmd/compile: update doc at top of iexport.go on the changes for typeparams Add in the documentation of the new declarations and type descriptors to support parameterized functions and types. Change-Id: Ia21c544caa704309cc8fd639f104c192e1786b72 Reviewed-on: https://go-review.googlesource.com/c/go/+/350951 Trust: Dan Scales Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/typecheck/iexport.go | 38 ++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go index bbdad09782..489306e1e6 100644 --- a/src/cmd/compile/internal/typecheck/iexport.go +++ b/src/cmd/compile/internal/typecheck/iexport.go @@ -63,8 +63,9 @@ // } // // type Func struct { -// Tag byte // 'F' +// Tag byte // 'F' or 'G' // Pos Pos +// TypeParams []typeOff // only present if Tag == 'G' // Signature Signature // } // @@ -75,8 +76,9 @@ // } // // type Type struct { -// Tag byte // 'T' +// Tag byte // 'T' or 'U' // Pos Pos +// TypeParams []typeOff // only present if Tag == 'U' // Underlying typeOff // // Methods []struct{ // omitted if Underlying is an interface type @@ -93,6 +95,12 @@ // Type typeOff // } // +// // "Automatic" declaration of each typeparam +// type TypeParam struct { +// Tag byte // 'P' +// Pos Pos +// Bound typeOff +// } // // typeOff means a uvarint that either indicates a predeclared type, // or an offset into the Data section. If the uvarint is less than @@ -104,7 +112,7 @@ // (*exportWriter).value for details. // // -// There are nine kinds of type descriptors, distinguished by an itag: +// There are twelve kinds of type descriptors, distinguished by an itag: // // type DefinedType struct { // Tag itag // definedType @@ -172,8 +180,30 @@ // } // } // +// // Reference to a type param declaration +// type TypeParamType struct { +// Tag itag // typeParamType +// Name stringOff +// PkgPath stringOff +// } +// +// // Instantiation of a generic type (like List[T2] or List[int]) +// type InstanceType struct { +// Tag itag // instanceType +// Pos pos +// TypeArgs []typeOff +// BaseType typeOff +// } +// +// type UnionType struct { +// Tag itag // interfaceType +// Terms []struct { +// tilde bool +// Type typeOff +// } +// } +// // -// TODO(danscales): fill in doc for 'type TypeParamType' and 'type InstType' // // type Signature struct { // Params []Param -- cgit v1.2.3-54-g00ecf From 6f35430faa622bad63a527c97e87b9291e480688 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Sep 2021 13:51:37 -0700 Subject: cmd/compile: allow rotates to be merged with logical ops on arm64 Fixes #48002 Change-Id: Ie3a157d55b291f5ac2ef4845e6ce4fefd84fc642 Reviewed-on: https://go-review.googlesource.com/c/go/+/350912 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/arm64/ssa.go | 11 + src/cmd/compile/internal/ssa/gen/ARM64.rules | 26 ++ src/cmd/compile/internal/ssa/gen/ARM64Ops.go | 8 + src/cmd/compile/internal/ssa/opGen.go | 124 +++++++ src/cmd/compile/internal/ssa/rewrite.go | 4 + src/cmd/compile/internal/ssa/rewriteARM64.go | 502 +++++++++++++++++++++++++++ src/cmd/internal/obj/arm64/a.out.go | 7 +- test/codegen/rotate.go | 23 ++ 8 files changed, 702 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index c5e7a08914..4770a0c488 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -315,6 +315,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt) case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA: genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64MVNshiftRO: + genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64ADDshiftLL, ssa.OpARM64SUBshiftLL, ssa.OpARM64ANDshiftLL, @@ -342,6 +344,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssa.OpARM64ORNshiftRA, ssa.OpARM64BICshiftRA: genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64ANDshiftRO, + ssa.OpARM64ORshiftRO, + ssa.OpARM64XORshiftRO, + ssa.OpARM64EONshiftRO, + ssa.OpARM64ORNshiftRO, + ssa.OpARM64BICshiftRO: + genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64MOVDconst: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_CONST @@ -389,6 +398,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt) case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA: genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt) + case ssa.OpARM64TSTshiftRO: + genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_ROR, v.AuxInt) case ssa.OpARM64MOVDaddr: p := s.Prog(arm64.AMOVD) p.From.Type = obj.TYPE_ADDR diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index 23ae3b1286..b722d1cf1c 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1601,6 +1601,7 @@ (MVN x:(SLLconst [c] y)) && clobberIfDead(x) => (MVNshiftLL [c] y) (MVN x:(SRLconst [c] y)) && clobberIfDead(x) => (MVNshiftRL [c] y) (MVN x:(SRAconst [c] y)) && clobberIfDead(x) => (MVNshiftRA [c] y) +(MVN x:(RORconst [c] y)) && clobberIfDead(x) => (MVNshiftRO [c] y) (ADD x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ADDshiftLL x0 y [c]) (ADD x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ADDshiftRL x0 y [c]) (ADD x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ADDshiftRA x0 y [c]) @@ -1610,21 +1611,27 @@ (AND x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ANDshiftLL x0 y [c]) (AND x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ANDshiftRL x0 y [c]) (AND x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ANDshiftRA x0 y [c]) +(AND x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ANDshiftRO x0 y [c]) (OR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORshiftLL x0 y [c]) // useful for combined load (OR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORshiftRL x0 y [c]) (OR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORshiftRA x0 y [c]) +(OR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORshiftRO x0 y [c]) (XOR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (XORshiftLL x0 y [c]) (XOR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (XORshiftRL x0 y [c]) (XOR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (XORshiftRA x0 y [c]) +(XOR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (XORshiftRO x0 y [c]) (BIC x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (BICshiftLL x0 y [c]) (BIC x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (BICshiftRL x0 y [c]) (BIC x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (BICshiftRA x0 y [c]) +(BIC x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (BICshiftRO x0 y [c]) (ORN x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORNshiftLL x0 y [c]) (ORN x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORNshiftRL x0 y [c]) (ORN x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORNshiftRA x0 y [c]) +(ORN x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORNshiftRO x0 y [c]) (EON x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (EONshiftLL x0 y [c]) (EON x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (EONshiftRL x0 y [c]) (EON x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (EONshiftRA x0 y [c]) +(EON x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (EONshiftRO x0 y [c]) (CMP x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (CMPshiftLL x0 y [c]) (CMP x0:(SLLconst [c] y) x1) && clobberIfDead(x0) => (InvertFlags (CMPshiftLL x1 y [c])) (CMP x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (CMPshiftRL x0 y [c]) @@ -1637,6 +1644,7 @@ (TST x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (TSTshiftLL x0 y [c]) (TST x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (TSTshiftRL x0 y [c]) (TST x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (TSTshiftRA x0 y [c]) +(TST x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (TSTshiftRO x0 y [c]) // prefer *const ops to *shift ops (ADDshiftLL (MOVDconst [c]) x [d]) => (ADDconst [c] (SLLconst x [d])) @@ -1645,12 +1653,15 @@ (ANDshiftLL (MOVDconst [c]) x [d]) => (ANDconst [c] (SLLconst x [d])) (ANDshiftRL (MOVDconst [c]) x [d]) => (ANDconst [c] (SRLconst x [d])) (ANDshiftRA (MOVDconst [c]) x [d]) => (ANDconst [c] (SRAconst x [d])) +(ANDshiftRO (MOVDconst [c]) x [d]) => (ANDconst [c] (RORconst x [d])) (ORshiftLL (MOVDconst [c]) x [d]) => (ORconst [c] (SLLconst x [d])) (ORshiftRL (MOVDconst [c]) x [d]) => (ORconst [c] (SRLconst x [d])) (ORshiftRA (MOVDconst [c]) x [d]) => (ORconst [c] (SRAconst x [d])) +(ORshiftRO (MOVDconst [c]) x [d]) => (ORconst [c] (RORconst x [d])) (XORshiftLL (MOVDconst [c]) x [d]) => (XORconst [c] (SLLconst x [d])) (XORshiftRL (MOVDconst [c]) x [d]) => (XORconst [c] (SRLconst x [d])) (XORshiftRA (MOVDconst [c]) x [d]) => (XORconst [c] (SRAconst x [d])) +(XORshiftRO (MOVDconst [c]) x [d]) => (XORconst [c] (RORconst x [d])) (CMPshiftLL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SLLconst x [d]))) (CMPshiftRL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRLconst x [d]))) (CMPshiftRA (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRAconst x [d]))) @@ -1660,11 +1671,13 @@ (TSTshiftLL (MOVDconst [c]) x [d]) => (TSTconst [c] (SLLconst x [d])) (TSTshiftRL (MOVDconst [c]) x [d]) => (TSTconst [c] (SRLconst x [d])) (TSTshiftRA (MOVDconst [c]) x [d]) => (TSTconst [c] (SRAconst x [d])) +(TSTshiftRO (MOVDconst [c]) x [d]) => (TSTconst [c] (RORconst x [d])) // constant folding in *shift ops (MVNshiftLL (MOVDconst [c]) [d]) => (MOVDconst [^int64(uint64(c)< (MOVDconst [^int64(uint64(c)>>uint64(d))]) (MVNshiftRA (MOVDconst [c]) [d]) => (MOVDconst [^(c>>uint64(d))]) +(MVNshiftRO (MOVDconst [c]) [d]) => (MOVDconst [^rotateRight64(c, d)]) (NEGshiftLL (MOVDconst [c]) [d]) => (MOVDconst [-int64(uint64(c)< (MOVDconst [-int64(uint64(c)>>uint64(d))]) (NEGshiftRA (MOVDconst [c]) [d]) => (MOVDconst [-(c>>uint64(d))]) @@ -1677,21 +1690,27 @@ (ANDshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [int64(uint64(c)< (ANDconst x [int64(uint64(c)>>uint64(d))]) (ANDshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [c>>uint64(d)]) +(ANDshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [rotateRight64(c, d)]) (ORshiftLL x (MOVDconst [c]) [d]) => (ORconst x [int64(uint64(c)< (ORconst x [int64(uint64(c)>>uint64(d))]) (ORshiftRA x (MOVDconst [c]) [d]) => (ORconst x [c>>uint64(d)]) +(ORshiftRO x (MOVDconst [c]) [d]) => (ORconst x [rotateRight64(c, d)]) (XORshiftLL x (MOVDconst [c]) [d]) => (XORconst x [int64(uint64(c)< (XORconst x [int64(uint64(c)>>uint64(d))]) (XORshiftRA x (MOVDconst [c]) [d]) => (XORconst x [c>>uint64(d)]) +(XORshiftRO x (MOVDconst [c]) [d]) => (XORconst x [rotateRight64(c, d)]) (BICshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [^int64(uint64(c)< (ANDconst x [^int64(uint64(c)>>uint64(d))]) (BICshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [^(c>>uint64(d))]) +(BICshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [^rotateRight64(c, d)]) (ORNshiftLL x (MOVDconst [c]) [d]) => (ORconst x [^int64(uint64(c)< (ORconst x [^int64(uint64(c)>>uint64(d))]) (ORNshiftRA x (MOVDconst [c]) [d]) => (ORconst x [^(c>>uint64(d))]) +(ORNshiftRO x (MOVDconst [c]) [d]) => (ORconst x [^rotateRight64(c, d)]) (EONshiftLL x (MOVDconst [c]) [d]) => (XORconst x [^int64(uint64(c)< (XORconst x [^int64(uint64(c)>>uint64(d))]) (EONshiftRA x (MOVDconst [c]) [d]) => (XORconst x [^(c>>uint64(d))]) +(EONshiftRO x (MOVDconst [c]) [d]) => (XORconst x [^rotateRight64(c, d)]) (CMPshiftLL x (MOVDconst [c]) [d]) => (CMPconst x [int64(uint64(c)< (CMPconst x [int64(uint64(c)>>uint64(d))]) (CMPshiftRA x (MOVDconst [c]) [d]) => (CMPconst x [c>>uint64(d)]) @@ -1701,6 +1720,7 @@ (TSTshiftLL x (MOVDconst [c]) [d]) => (TSTconst x [int64(uint64(c)< (TSTconst x [int64(uint64(c)>>uint64(d))]) (TSTshiftRA x (MOVDconst [c]) [d]) => (TSTconst x [c>>uint64(d)]) +(TSTshiftRO x (MOVDconst [c]) [d]) => (TSTconst x [rotateRight64(c, d)]) // simplification with *shift ops (SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) @@ -1709,21 +1729,27 @@ (ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y (ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y (ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y +(ANDshiftRO x y:(RORconst x [c]) [d]) && c==d => y (ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y (ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y (ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y +(ORshiftRO x y:(RORconst x [c]) [d]) && c==d => y (XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) (XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) (XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) +(XORshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [0]) (BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) (BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) (BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) +(BICshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [0]) (EONshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) (EONshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) (EONshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) +(EONshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [-1]) (ORNshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) (ORNshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) (ORNshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) +(ORNshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [-1]) // Generate rotates with const shift (ADDshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go index 41231fb809..e3ebb6e1af 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go @@ -302,6 +302,7 @@ func init() { {name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "MVNshiftRO", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "NEGshiftLL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "NEGshiftRA", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63. @@ -314,21 +315,27 @@ func init() { {name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "ANDshiftRO", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. + {name: "ORshiftRO", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63. {name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. + {name: "XORshiftRO", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63. {name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "BICshiftRO", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "EONshiftLL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "EONshiftRA", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "EONshiftRO", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "ORNshiftLL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63. {name: "ORNshiftRA", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63. + {name: "ORNshiftRO", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63. {name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63. {name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63. @@ -338,6 +345,7 @@ func init() { {name: "TSTshiftLL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1<>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63. {name: "TSTshiftRA", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63. + {name: "TSTshiftRO", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1 ROR auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63. // bitfield ops // for all bitfield ops lsb is auxInt>>8, width is auxInt&0xff diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 4ec13a6629..eb7e4b91bb 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -1412,6 +1412,7 @@ const ( OpARM64MVNshiftLL OpARM64MVNshiftRL OpARM64MVNshiftRA + OpARM64MVNshiftRO OpARM64NEGshiftLL OpARM64NEGshiftRL OpARM64NEGshiftRA @@ -1424,21 +1425,27 @@ const ( OpARM64ANDshiftLL OpARM64ANDshiftRL OpARM64ANDshiftRA + OpARM64ANDshiftRO OpARM64ORshiftLL OpARM64ORshiftRL OpARM64ORshiftRA + OpARM64ORshiftRO OpARM64XORshiftLL OpARM64XORshiftRL OpARM64XORshiftRA + OpARM64XORshiftRO OpARM64BICshiftLL OpARM64BICshiftRL OpARM64BICshiftRA + OpARM64BICshiftRO OpARM64EONshiftLL OpARM64EONshiftRL OpARM64EONshiftRA + OpARM64EONshiftRO OpARM64ORNshiftLL OpARM64ORNshiftRL OpARM64ORNshiftRA + OpARM64ORNshiftRO OpARM64CMPshiftLL OpARM64CMPshiftRL OpARM64CMPshiftRA @@ -1448,6 +1455,7 @@ const ( OpARM64TSTshiftLL OpARM64TSTshiftRL OpARM64TSTshiftRA + OpARM64TSTshiftRO OpARM64BFI OpARM64BFXIL OpARM64SBFIZ @@ -18807,6 +18815,20 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "MVNshiftRO", + auxType: auxInt64, + argLen: 1, + asm: arm64.AMVN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "NEGshiftLL", auxType: auxInt64, @@ -18984,6 +19006,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ANDshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AAND, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "ORshiftLL", auxType: auxInt64, @@ -19029,6 +19066,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ORshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "XORshiftLL", auxType: auxInt64, @@ -19074,6 +19126,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "XORshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEOR, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "BICshiftLL", auxType: auxInt64, @@ -19119,6 +19186,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "BICshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.ABIC, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "EONshiftLL", auxType: auxInt64, @@ -19164,6 +19246,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "EONshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AEON, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "ORNshiftLL", auxType: auxInt64, @@ -19209,6 +19306,21 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "ORNshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.AORN, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + outputs: []outputInfo{ + {0, 670826495}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 R30 + }, + }, + }, { name: "CMPshiftLL", auxType: auxInt64, @@ -19317,6 +19429,18 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "TSTshiftRO", + auxType: auxInt64, + argLen: 2, + asm: arm64.ATST, + reg: regInfo{ + inputs: []inputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + }, + }, { name: "BFI", auxType: auxARM64BitField, diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 162d42773a..79f9efaebf 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -1567,6 +1567,10 @@ func rotateLeft32(v, rotate int64) int64 { return int64(bits.RotateLeft32(uint32(v), int(rotate))) } +func rotateRight64(v, rotate int64) int64 { + return int64(bits.RotateLeft64(uint64(v), int(-rotate))) +} + // encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format. func armBFAuxInt(lsb, width int64) arm64BitField { if lsb < 0 || lsb > 63 { diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 661714307a..06cc5a62b2 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -29,6 +29,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ANDshiftRA(v) case OpARM64ANDshiftRL: return rewriteValueARM64_OpARM64ANDshiftRL(v) + case OpARM64ANDshiftRO: + return rewriteValueARM64_OpARM64ANDshiftRO(v) case OpARM64BIC: return rewriteValueARM64_OpARM64BIC(v) case OpARM64BICshiftLL: @@ -37,6 +39,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64BICshiftRA(v) case OpARM64BICshiftRL: return rewriteValueARM64_OpARM64BICshiftRL(v) + case OpARM64BICshiftRO: + return rewriteValueARM64_OpARM64BICshiftRO(v) case OpARM64CMN: return rewriteValueARM64_OpARM64CMN(v) case OpARM64CMNW: @@ -89,6 +93,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64EONshiftRA(v) case OpARM64EONshiftRL: return rewriteValueARM64_OpARM64EONshiftRL(v) + case OpARM64EONshiftRO: + return rewriteValueARM64_OpARM64EONshiftRO(v) case OpARM64Equal: return rewriteValueARM64_OpARM64Equal(v) case OpARM64FADDD: @@ -295,6 +301,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64MVNshiftRA(v) case OpARM64MVNshiftRL: return rewriteValueARM64_OpARM64MVNshiftRL(v) + case OpARM64MVNshiftRO: + return rewriteValueARM64_OpARM64MVNshiftRO(v) case OpARM64NEG: return rewriteValueARM64_OpARM64NEG(v) case OpARM64NEGshiftLL: @@ -315,6 +323,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ORNshiftRA(v) case OpARM64ORNshiftRL: return rewriteValueARM64_OpARM64ORNshiftRL(v) + case OpARM64ORNshiftRO: + return rewriteValueARM64_OpARM64ORNshiftRO(v) case OpARM64ORconst: return rewriteValueARM64_OpARM64ORconst(v) case OpARM64ORshiftLL: @@ -323,6 +333,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64ORshiftRA(v) case OpARM64ORshiftRL: return rewriteValueARM64_OpARM64ORshiftRL(v) + case OpARM64ORshiftRO: + return rewriteValueARM64_OpARM64ORshiftRO(v) case OpARM64ROR: return rewriteValueARM64_OpARM64ROR(v) case OpARM64RORW: @@ -371,6 +383,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64TSTshiftRA(v) case OpARM64TSTshiftRL: return rewriteValueARM64_OpARM64TSTshiftRL(v) + case OpARM64TSTshiftRO: + return rewriteValueARM64_OpARM64TSTshiftRO(v) case OpARM64UBFIZ: return rewriteValueARM64_OpARM64UBFIZ(v) case OpARM64UBFX: @@ -393,6 +407,8 @@ func rewriteValueARM64(v *Value) bool { return rewriteValueARM64_OpARM64XORshiftRA(v) case OpARM64XORshiftRL: return rewriteValueARM64_OpARM64XORshiftRL(v) + case OpARM64XORshiftRO: + return rewriteValueARM64_OpARM64XORshiftRO(v) case OpAbs: v.Op = OpARM64FABSD return true @@ -2114,6 +2130,28 @@ func rewriteValueARM64_OpARM64AND(v *Value) bool { } break } + // match: (AND x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ANDshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64ANDshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } return false } func rewriteValueARM64_OpARM64ANDconst(v *Value) bool { @@ -2403,6 +2441,60 @@ func rewriteValueARM64_OpARM64ANDshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64ANDshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (ANDshiftRO (MOVDconst [c]) x [d]) + // result: (ANDconst [c] (RORconst x [d])) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (ANDshiftRO x (MOVDconst [c]) [d]) + // result: (ANDconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (ANDshiftRO x y:(RORconst x [c]) [d]) + // cond: c==d + // result: y + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + y := v_1 + if y.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(y.AuxInt) + if x != y.Args[0] || !(c == d) { + break + } + v.copyOf(y) + return true + } + return false +} func rewriteValueARM64_OpARM64BIC(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -2487,6 +2579,25 @@ func rewriteValueARM64_OpARM64BIC(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (BIC x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (BICshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64BICshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool { @@ -2597,6 +2708,42 @@ func rewriteValueARM64_OpARM64BICshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64BICshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (BICshiftRO x (MOVDconst [c]) [d]) + // result: (ANDconst x [^rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ANDconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (BICshiftRO x (RORconst x [c]) [d]) + // cond: c==d + // result: (MOVDconst [0]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if x != v_1.Args[0] || !(c == d) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} func rewriteValueARM64_OpARM64CMN(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -3937,6 +4084,25 @@ func rewriteValueARM64_OpARM64EON(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (EON x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (EONshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64EONshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool { @@ -4047,6 +4213,42 @@ func rewriteValueARM64_OpARM64EONshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64EONshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (EONshiftRO x (MOVDconst [c]) [d]) + // result: (XORconst x [^rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (EONshiftRO x (RORconst x [c]) [d]) + // cond: c==d + // result: (MOVDconst [-1]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if x != v_1.Args[0] || !(c == d) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + return false +} func rewriteValueARM64_OpARM64Equal(v *Value) bool { v_0 := v.Args[0] // match: (Equal (FlagConstant [fc])) @@ -15615,6 +15817,24 @@ func rewriteValueARM64_OpARM64MVN(v *Value) bool { v.AddArg(y) return true } + // match: (MVN x:(RORconst [c] y)) + // cond: clobberIfDead(x) + // result: (MVNshiftRO [c] y) + for { + x := v_0 + if x.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x.AuxInt) + y := x.Args[0] + if !(clobberIfDead(x)) { + break + } + v.reset(OpARM64MVNshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg(y) + return true + } return false } func rewriteValueARM64_OpARM64MVNshiftLL(v *Value) bool { @@ -15665,6 +15885,22 @@ func rewriteValueARM64_OpARM64MVNshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64MVNshiftRO(v *Value) bool { + v_0 := v.Args[0] + // match: (MVNshiftRO (MOVDconst [c]) [d]) + // result: (MOVDconst [^rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + return true + } + return false +} func rewriteValueARM64_OpARM64NEG(v *Value) bool { v_0 := v.Args[0] // match: (NEG (MUL x y)) @@ -15954,6 +16190,28 @@ func rewriteValueARM64_OpARM64OR(v *Value) bool { } break } + // match: (OR x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ORshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64ORshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } // match: (OR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) // cond: cc == OpARM64LessThanU // result: (ROR x (NEG y)) @@ -17923,6 +18181,25 @@ func rewriteValueARM64_OpARM64ORN(v *Value) bool { v.AddArg2(x0, y) return true } + // match: (ORN x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (ORNshiftRO x0 y [c]) + for { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + break + } + v.reset(OpARM64ORNshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } return false } func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool { @@ -18033,6 +18310,42 @@ func rewriteValueARM64_OpARM64ORNshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64ORNshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + // match: (ORNshiftRO x (MOVDconst [c]) [d]) + // result: (ORconst x [^rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(^rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (ORNshiftRO x (RORconst x [c]) [d]) + // cond: c==d + // result: (MOVDconst [-1]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if x != v_1.Args[0] || !(c == d) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(-1) + return true + } + return false +} func rewriteValueARM64_OpARM64ORconst(v *Value) bool { v_0 := v.Args[0] // match: (ORconst [0] x) @@ -19970,6 +20283,60 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64ORshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (ORshiftRO (MOVDconst [c]) x [d]) + // result: (ORconst [c] (RORconst x [d])) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (ORshiftRO x (MOVDconst [c]) [d]) + // result: (ORconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64ORconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (ORshiftRO x y:(RORconst x [c]) [d]) + // cond: c==d + // result: y + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + y := v_1 + if y.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(y.AuxInt) + if x != y.Args[0] || !(c == d) { + break + } + v.copyOf(y) + return true + } + return false +} func rewriteValueARM64_OpARM64ROR(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] @@ -21198,6 +21565,28 @@ func rewriteValueARM64_OpARM64TST(v *Value) bool { } break } + // match: (TST x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (TSTshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64TSTshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } return false } func rewriteValueARM64_OpARM64TSTW(v *Value) bool { @@ -21364,6 +21753,43 @@ func rewriteValueARM64_OpARM64TSTshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64TSTshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (TSTshiftRO (MOVDconst [c]) x [d]) + // result: (TSTconst [c] (RORconst x [d])) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64TSTconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (TSTshiftRO x (MOVDconst [c]) [d]) + // result: (TSTconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64TSTconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + return false +} func rewriteValueARM64_OpARM64UBFIZ(v *Value) bool { v_0 := v.Args[0] // match: (UBFIZ [bfc] (SLLconst [sc] x)) @@ -21823,6 +22249,28 @@ func rewriteValueARM64_OpARM64XOR(v *Value) bool { } break } + // match: (XOR x0 x1:(RORconst [c] y)) + // cond: clobberIfDead(x1) + // result: (XORshiftRO x0 y [c]) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + x0 := v_0 + x1 := v_1 + if x1.Op != OpARM64RORconst { + continue + } + c := auxIntToInt64(x1.AuxInt) + y := x1.Args[0] + if !(clobberIfDead(x1)) { + continue + } + v.reset(OpARM64XORshiftRO) + v.AuxInt = int64ToAuxInt(c) + v.AddArg2(x0, y) + return true + } + break + } // match: (XOR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y))))) // cond: cc == OpARM64LessThanU // result: (ROR x (NEG y)) @@ -22512,6 +22960,60 @@ func rewriteValueARM64_OpARM64XORshiftRL(v *Value) bool { } return false } +func rewriteValueARM64_OpARM64XORshiftRO(v *Value) bool { + v_1 := v.Args[1] + v_0 := v.Args[0] + b := v.Block + // match: (XORshiftRO (MOVDconst [c]) x [d]) + // result: (XORconst [c] (RORconst x [d])) + for { + d := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_0.AuxInt) + x := v_1 + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(c) + v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type) + v0.AuxInt = int64ToAuxInt(d) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (XORshiftRO x (MOVDconst [c]) [d]) + // result: (XORconst x [rotateRight64(c, d)]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64MOVDconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + v.reset(OpARM64XORconst) + v.AuxInt = int64ToAuxInt(rotateRight64(c, d)) + v.AddArg(x) + return true + } + // match: (XORshiftRO x (RORconst x [c]) [d]) + // cond: c==d + // result: (MOVDconst [0]) + for { + d := auxIntToInt64(v.AuxInt) + x := v_0 + if v_1.Op != OpARM64RORconst { + break + } + c := auxIntToInt64(v_1.AuxInt) + if x != v_1.Args[0] || !(c == d) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(0) + return true + } + return false +} func rewriteValueARM64_OpAddr(v *Value) bool { v_0 := v.Args[0] // match: (Addr {sym} base) diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index bf75bb4a89..aa7c54df9a 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -1060,9 +1060,10 @@ const ( const ( // shift types - SHIFT_LL = 0 << 22 - SHIFT_LR = 1 << 22 - SHIFT_AR = 2 << 22 + SHIFT_LL = 0 << 22 + SHIFT_LR = 1 << 22 + SHIFT_AR = 2 << 22 + SHIFT_ROR = 3 << 22 ) // Arrangement for ARM64 SIMD instructions diff --git a/test/codegen/rotate.go b/test/codegen/rotate.go index 70489a2adc..204efaeafc 100644 --- a/test/codegen/rotate.go +++ b/test/codegen/rotate.go @@ -226,3 +226,26 @@ func checkMaskedRotate32(a []uint32, r int) { a[i] = bits.RotateLeft32(a[3], 4) & 0xFFF00FFF i++ } + +// combined arithmetic and rotate on arm64 +func checkArithmeticWithRotate(a *[1000]uint64) { + // arm64: "AND\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[2] = a[1] & bits.RotateLeft64(a[0], 13) + // arm64: "ORR\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[5] = a[4] | bits.RotateLeft64(a[3], 13) + // arm64: "EOR\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[8] = a[7] ^ bits.RotateLeft64(a[6], 13) + // arm64: "MVN\tR[0-9]+@>51, R[0-9]+" + a[10] = ^bits.RotateLeft64(a[9], 13) + // arm64: "BIC\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[13] = a[12] &^ bits.RotateLeft64(a[11], 13) + // arm64: "EON\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[16] = a[15] ^ ^bits.RotateLeft64(a[14], 13) + // arm64: "ORN\tR[0-9]+@>51, R[0-9]+, R[0-9]+" + a[19] = a[18] | ^bits.RotateLeft64(a[17], 13) + // arm64: "TST\tR[0-9]+@>51, R[0-9]+" + if a[18]&bits.RotateLeft64(a[19], 13) == 0 { + a[20] = 1 + } + +} -- cgit v1.2.3-54-g00ecf From 9ebe7c8ec6e95b73a7b0e480fe10f2188ef4928a Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Thu, 16 Sep 2021 09:42:06 -0700 Subject: go/test: add a test for issue 48344 This was fixed by https://go-review.googlesource.com/c/go/+/349613 and https://go-review.googlesource.com/c/go/+/349614 Fixes #48344 Change-Id: I4c62109fd34b20566b07fcca87fb3946a5702fef Reviewed-on: https://go-review.googlesource.com/c/go/+/350309 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Dan Scales --- test/typeparam/issue48344.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/typeparam/issue48344.go diff --git a/test/typeparam/issue48344.go b/test/typeparam/issue48344.go new file mode 100644 index 0000000000..7ea539cfcc --- /dev/null +++ b/test/typeparam/issue48344.go @@ -0,0 +1,26 @@ +// run -gcflags=-G=3 + +// Copyright 2021 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 main + +type G[T any] interface { + g() +} + +type Foo[T any] struct { +} + +func (foo *Foo[T]) g() { + +} + +func f[T any]() { + v := []G[T]{} + v = append(v, &Foo[T]{}) +} +func main() { + f[int]() +} -- cgit v1.2.3-54-g00ecf From eff27e858b771bf5e0b5e7e836827c7d2941e6d4 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Sep 2021 21:09:57 -0700 Subject: cmd/compile: ensure constant shift amounts are in range for arm Ensure constant shift amounts are in the range [0-31]. When shift amounts are out of range, bad things happen. Shift amounts out of range occur when lowering 64-bit shifts (we take an in-range shift s in [0-63] and calculate s-32 and 32-s, both of which might be out of [0-31]). The constant shift operations themselves still work, but their shift amounts get copied unmolested to operations like ORshiftLL which use only the low 5 bits. That changes an operation like <<100 which unconditionally produces 0, to <<4, which doesn't. Fixes #48476 Change-Id: I87363ef2b4ceaf3b2e316426064626efdfbb8ee3 Reviewed-on: https://go-review.googlesource.com/c/go/+/350969 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/ssa/gen/ARM.rules | 114 ++++++------ src/cmd/compile/internal/ssa/gen/ARMOps.go | 9 +- src/cmd/compile/internal/ssa/gen/genericOps.go | 2 +- src/cmd/compile/internal/ssa/rewriteARM.go | 240 ++++++++++++++++++++++++- test/fixedbugs/issue48476.go | 21 +++ 5 files changed, 318 insertions(+), 68 deletions(-) create mode 100644 test/fixedbugs/issue48476.go diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules index 8a755b404b..a6686237be 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -498,9 +498,9 @@ (XOR x (MOVWconst [c])) => (XORconst [c] x) (BIC x (MOVWconst [c])) => (BICconst [c] x) -(SLL x (MOVWconst [c])) => (SLLconst x [c&31]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=32) -(SRL x (MOVWconst [c])) => (SRLconst x [c&31]) -(SRA x (MOVWconst [c])) => (SRAconst x [c&31]) +(SLL x (MOVWconst [c])) && 0 <= c && c < 32 => (SLLconst x [c]) +(SRL x (MOVWconst [c])) && 0 <= c && c < 32 => (SRLconst x [c]) +(SRA x (MOVWconst [c])) && 0 <= c && c < 32 => (SRAconst x [c]) (CMP x (MOVWconst [c])) => (CMPconst [c] x) (CMP (MOVWconst [c]) x) => (InvertFlags (CMPconst [c] x)) @@ -1075,60 +1075,60 @@ (CMNshiftRL x (MOVWconst [c]) [d]) => (CMNconst x [int32(uint32(c)>>uint64(d))]) (CMNshiftRA x (MOVWconst [c]) [d]) => (CMNconst x [c>>uint64(d)]) -(ADDshiftLLreg x y (MOVWconst [c])) => (ADDshiftLL x y [c]) -(ADDshiftRLreg x y (MOVWconst [c])) => (ADDshiftRL x y [c]) -(ADDshiftRAreg x y (MOVWconst [c])) => (ADDshiftRA x y [c]) -(ADCshiftLLreg x y (MOVWconst [c]) flags) => (ADCshiftLL x y [c] flags) -(ADCshiftRLreg x y (MOVWconst [c]) flags) => (ADCshiftRL x y [c] flags) -(ADCshiftRAreg x y (MOVWconst [c]) flags) => (ADCshiftRA x y [c] flags) -(ADDSshiftLLreg x y (MOVWconst [c])) => (ADDSshiftLL x y [c]) -(ADDSshiftRLreg x y (MOVWconst [c])) => (ADDSshiftRL x y [c]) -(ADDSshiftRAreg x y (MOVWconst [c])) => (ADDSshiftRA x y [c]) -(SUBshiftLLreg x y (MOVWconst [c])) => (SUBshiftLL x y [c]) -(SUBshiftRLreg x y (MOVWconst [c])) => (SUBshiftRL x y [c]) -(SUBshiftRAreg x y (MOVWconst [c])) => (SUBshiftRA x y [c]) -(SBCshiftLLreg x y (MOVWconst [c]) flags) => (SBCshiftLL x y [c] flags) -(SBCshiftRLreg x y (MOVWconst [c]) flags) => (SBCshiftRL x y [c] flags) -(SBCshiftRAreg x y (MOVWconst [c]) flags) => (SBCshiftRA x y [c] flags) -(SUBSshiftLLreg x y (MOVWconst [c])) => (SUBSshiftLL x y [c]) -(SUBSshiftRLreg x y (MOVWconst [c])) => (SUBSshiftRL x y [c]) -(SUBSshiftRAreg x y (MOVWconst [c])) => (SUBSshiftRA x y [c]) -(RSBshiftLLreg x y (MOVWconst [c])) => (RSBshiftLL x y [c]) -(RSBshiftRLreg x y (MOVWconst [c])) => (RSBshiftRL x y [c]) -(RSBshiftRAreg x y (MOVWconst [c])) => (RSBshiftRA x y [c]) -(RSCshiftLLreg x y (MOVWconst [c]) flags) => (RSCshiftLL x y [c] flags) -(RSCshiftRLreg x y (MOVWconst [c]) flags) => (RSCshiftRL x y [c] flags) -(RSCshiftRAreg x y (MOVWconst [c]) flags) => (RSCshiftRA x y [c] flags) -(RSBSshiftLLreg x y (MOVWconst [c])) => (RSBSshiftLL x y [c]) -(RSBSshiftRLreg x y (MOVWconst [c])) => (RSBSshiftRL x y [c]) -(RSBSshiftRAreg x y (MOVWconst [c])) => (RSBSshiftRA x y [c]) -(ANDshiftLLreg x y (MOVWconst [c])) => (ANDshiftLL x y [c]) -(ANDshiftRLreg x y (MOVWconst [c])) => (ANDshiftRL x y [c]) -(ANDshiftRAreg x y (MOVWconst [c])) => (ANDshiftRA x y [c]) -(ORshiftLLreg x y (MOVWconst [c])) => (ORshiftLL x y [c]) -(ORshiftRLreg x y (MOVWconst [c])) => (ORshiftRL x y [c]) -(ORshiftRAreg x y (MOVWconst [c])) => (ORshiftRA x y [c]) -(XORshiftLLreg x y (MOVWconst [c])) => (XORshiftLL x y [c]) -(XORshiftRLreg x y (MOVWconst [c])) => (XORshiftRL x y [c]) -(XORshiftRAreg x y (MOVWconst [c])) => (XORshiftRA x y [c]) -(BICshiftLLreg x y (MOVWconst [c])) => (BICshiftLL x y [c]) -(BICshiftRLreg x y (MOVWconst [c])) => (BICshiftRL x y [c]) -(BICshiftRAreg x y (MOVWconst [c])) => (BICshiftRA x y [c]) -(MVNshiftLLreg x (MOVWconst [c])) => (MVNshiftLL x [c]) -(MVNshiftRLreg x (MOVWconst [c])) => (MVNshiftRL x [c]) -(MVNshiftRAreg x (MOVWconst [c])) => (MVNshiftRA x [c]) -(CMPshiftLLreg x y (MOVWconst [c])) => (CMPshiftLL x y [c]) -(CMPshiftRLreg x y (MOVWconst [c])) => (CMPshiftRL x y [c]) -(CMPshiftRAreg x y (MOVWconst [c])) => (CMPshiftRA x y [c]) -(TSTshiftLLreg x y (MOVWconst [c])) => (TSTshiftLL x y [c]) -(TSTshiftRLreg x y (MOVWconst [c])) => (TSTshiftRL x y [c]) -(TSTshiftRAreg x y (MOVWconst [c])) => (TSTshiftRA x y [c]) -(TEQshiftLLreg x y (MOVWconst [c])) => (TEQshiftLL x y [c]) -(TEQshiftRLreg x y (MOVWconst [c])) => (TEQshiftRL x y [c]) -(TEQshiftRAreg x y (MOVWconst [c])) => (TEQshiftRA x y [c]) -(CMNshiftLLreg x y (MOVWconst [c])) => (CMNshiftLL x y [c]) -(CMNshiftRLreg x y (MOVWconst [c])) => (CMNshiftRL x y [c]) -(CMNshiftRAreg x y (MOVWconst [c])) => (CMNshiftRA x y [c]) +(ADDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftLL x y [c]) +(ADDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRL x y [c]) +(ADDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRA x y [c]) +(ADCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftLL x y [c] flags) +(ADCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRL x y [c] flags) +(ADCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRA x y [c] flags) +(ADDSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftLL x y [c]) +(ADDSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRL x y [c]) +(ADDSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRA x y [c]) +(SUBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftLL x y [c]) +(SUBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRL x y [c]) +(SUBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRA x y [c]) +(SBCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftLL x y [c] flags) +(SBCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRL x y [c] flags) +(SBCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRA x y [c] flags) +(SUBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftLL x y [c]) +(SUBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRL x y [c]) +(SUBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRA x y [c]) +(RSBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftLL x y [c]) +(RSBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRL x y [c]) +(RSBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRA x y [c]) +(RSCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftLL x y [c] flags) +(RSCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRL x y [c] flags) +(RSCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRA x y [c] flags) +(RSBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftLL x y [c]) +(RSBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRL x y [c]) +(RSBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRA x y [c]) +(ANDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftLL x y [c]) +(ANDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRL x y [c]) +(ANDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRA x y [c]) +(ORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftLL x y [c]) +(ORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRL x y [c]) +(ORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRA x y [c]) +(XORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftLL x y [c]) +(XORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRL x y [c]) +(XORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRA x y [c]) +(BICshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftLL x y [c]) +(BICshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRL x y [c]) +(BICshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRA x y [c]) +(MVNshiftLLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftLL x [c]) +(MVNshiftRLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRL x [c]) +(MVNshiftRAreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRA x [c]) +(CMPshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftLL x y [c]) +(CMPshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRL x y [c]) +(CMPshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRA x y [c]) +(TSTshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftLL x y [c]) +(TSTshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRL x y [c]) +(TSTshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRA x y [c]) +(TEQshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftLL x y [c]) +(TEQshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRL x y [c]) +(TEQshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRA x y [c]) +(CMNshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftLL x y [c]) +(CMNshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRL x y [c]) +(CMNshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRA x y [c]) // Generate rotates (ADDshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x) diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go index eea0703f2c..75ba769724 100644 --- a/src/cmd/compile/internal/ssa/gen/ARMOps.go +++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go @@ -228,14 +228,15 @@ func init() { // shifts {name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 256 - {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt + {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, 0 <= auxInt < 32 {name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 256 - {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned + {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned, 0 <= auxInt < 32 {name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 256 - {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed + {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, 0 <= auxInt < 32 {name: "SRR", argLength: 2, reg: gp21}, // arg0 right rotate by arg1 bits - {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits + {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits, 0 <= auxInt < 32 + // auxInt for all of these satisfy 0 <= auxInt < 32 {name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1<>auxInt, unsigned shift {name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index a0166f58f4..984552900f 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -106,7 +106,7 @@ var genericOps = []opData{ // For shifts, AxB means the shifted value has A bits and the shift amount has B bits. // Shift amounts are considered unsigned. - // If arg1 is known to be less than the number of bits in arg0, + // If arg1 is known to be nonnegative and less than the number of bits in arg0, // then auxInt may be set to 1. // This enables better code generation on some platforms. {name: "Lsh8x8", argLength: 2, aux: "Bool"}, // arg0 << arg1 diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index 6807507218..2c536e1a52 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -1124,6 +1124,7 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool { return true } // match: (ADCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftLL x y [c] flags) for { x := v_0 @@ -1133,6 +1134,9 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1204,6 +1208,7 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool { return true } // match: (ADCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftRA x y [c] flags) for { x := v_0 @@ -1213,6 +1218,9 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1284,6 +1292,7 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool { return true } // match: (ADCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (ADCshiftRL x y [c] flags) for { x := v_0 @@ -1293,6 +1302,9 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -1745,6 +1757,7 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool { return true } // match: (ADDSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftLL x y [c]) for { x := v_0 @@ -1753,6 +1766,9 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -1819,6 +1835,7 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool { return true } // match: (ADDSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftRA x y [c]) for { x := v_0 @@ -1827,6 +1844,9 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -1893,6 +1913,7 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool { return true } // match: (ADDSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDSshiftRL x y [c]) for { x := v_0 @@ -1901,6 +1922,9 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2129,6 +2153,7 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool { return true } // match: (ADDshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftLL x y [c]) for { x := v_0 @@ -2137,6 +2162,9 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2203,6 +2231,7 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool { return true } // match: (ADDshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftRA x y [c]) for { x := v_0 @@ -2211,6 +2240,9 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2293,6 +2325,7 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool { return true } // match: (ADDshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ADDshiftRL x y [c]) for { x := v_0 @@ -2301,6 +2334,9 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMADDshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2660,6 +2696,7 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool { return true } // match: (ANDshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftLL x y [c]) for { x := v_0 @@ -2668,6 +2705,9 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2751,6 +2791,7 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool { return true } // match: (ANDshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftRA x y [c]) for { x := v_0 @@ -2759,6 +2800,9 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -2842,6 +2886,7 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool { return true } // match: (ANDshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ANDshiftRL x y [c]) for { x := v_0 @@ -2850,6 +2895,9 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMANDshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3120,6 +3168,7 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftLL x y [c]) for { x := v_0 @@ -3128,6 +3177,9 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3176,6 +3228,7 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftRA x y [c]) for { x := v_0 @@ -3184,6 +3237,9 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3232,6 +3288,7 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (BICshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (BICshiftRL x y [c]) for { x := v_0 @@ -3240,6 +3297,9 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMBICshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3442,6 +3502,7 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool { return true } // match: (CMNshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftLL x y [c]) for { x := v_0 @@ -3450,6 +3511,9 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3516,6 +3580,7 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool { return true } // match: (CMNshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftRA x y [c]) for { x := v_0 @@ -3524,6 +3589,9 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -3590,6 +3658,7 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool { return true } // match: (CMNshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMNshiftRL x y [c]) for { x := v_0 @@ -3598,6 +3667,9 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMNshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4095,6 +4167,7 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool { return true } // match: (CMPshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftLL x y [c]) for { x := v_0 @@ -4103,6 +4176,9 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4173,6 +4249,7 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool { return true } // match: (CMPshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftRA x y [c]) for { x := v_0 @@ -4181,6 +4258,9 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -4251,6 +4331,7 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool { return true } // match: (CMPshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (CMPshiftRL x y [c]) for { x := v_0 @@ -4259,6 +4340,9 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMCMPshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8106,6 +8190,7 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftLLreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftLL x [c]) for { x := v_0 @@ -8113,6 +8198,9 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8140,6 +8228,7 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftRAreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftRA x [c]) for { x := v_0 @@ -8147,6 +8236,9 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8174,6 +8266,7 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (MVNshiftRLreg x (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (MVNshiftRL x [c]) for { x := v_0 @@ -8181,6 +8274,9 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMMVNshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg(x) @@ -8602,6 +8698,7 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool { return true } // match: (ORshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftLL x y [c]) for { x := v_0 @@ -8610,6 +8707,9 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8693,6 +8793,7 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool { return true } // match: (ORshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftRA x y [c]) for { x := v_0 @@ -8701,6 +8802,9 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -8800,6 +8904,7 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool { return true } // match: (ORshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (ORshiftRL x y [c]) for { x := v_0 @@ -8808,6 +8913,9 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMORshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9095,6 +9203,7 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool { return true } // match: (RSBSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftLL x y [c]) for { x := v_0 @@ -9103,6 +9212,9 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9169,6 +9281,7 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool { return true } // match: (RSBSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftRA x y [c]) for { x := v_0 @@ -9177,6 +9290,9 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9243,6 +9359,7 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool { return true } // match: (RSBSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBSshiftRL x y [c]) for { x := v_0 @@ -9251,6 +9368,9 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9392,6 +9512,7 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool { return true } // match: (RSBshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftLL x y [c]) for { x := v_0 @@ -9400,6 +9521,9 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9483,6 +9607,7 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool { return true } // match: (RSBshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftRA x y [c]) for { x := v_0 @@ -9491,6 +9616,9 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9574,6 +9702,7 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool { return true } // match: (RSBshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (RSBshiftRL x y [c]) for { x := v_0 @@ -9582,6 +9711,9 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSBshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -9688,6 +9820,7 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool { return true } // match: (RSCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftLL x y [c] flags) for { x := v_0 @@ -9697,6 +9830,9 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -9768,6 +9904,7 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool { return true } // match: (RSCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftRA x y [c] flags) for { x := v_0 @@ -9777,6 +9914,9 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -9848,6 +9988,7 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool { return true } // match: (RSCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (RSCshiftRL x y [c] flags) for { x := v_0 @@ -9857,6 +9998,9 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMRSCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10171,6 +10315,7 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool { return true } // match: (SBCshiftLLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftLL x y [c] flags) for { x := v_0 @@ -10180,6 +10325,9 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10251,6 +10399,7 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool { return true } // match: (SBCshiftRAreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftRA x y [c] flags) for { x := v_0 @@ -10260,6 +10409,9 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10331,6 +10483,7 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool { return true } // match: (SBCshiftRLreg x y (MOVWconst [c]) flags) + // cond: 0 <= c && c < 32 // result: (SBCshiftRL x y [c] flags) for { x := v_0 @@ -10340,6 +10493,9 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool { } c := auxIntToInt32(v_2.AuxInt) flags := v_3 + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSBCshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg3(x, y, flags) @@ -10351,15 +10507,19 @@ func rewriteValueARM_OpARMSLL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SLL x (MOVWconst [c])) - // result: (SLLconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SLLconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSLLconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -10385,15 +10545,19 @@ func rewriteValueARM_OpARMSRA(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SRA x (MOVWconst [c])) - // result: (SRAconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SRAconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSRAconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -10477,15 +10641,19 @@ func rewriteValueARM_OpARMSRL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] // match: (SRL x (MOVWconst [c])) - // result: (SRLconst x [c&31]) + // cond: 0 <= c && c < 32 + // result: (SRLconst x [c]) for { x := v_0 if v_1.Op != OpARMMOVWconst { break } c := auxIntToInt32(v_1.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSRLconst) - v.AuxInt = int32ToAuxInt(c & 31) + v.AuxInt = int32ToAuxInt(c) v.AddArg(x) return true } @@ -11081,6 +11249,7 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool { return true } // match: (SUBSshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftLL x y [c]) for { x := v_0 @@ -11089,6 +11258,9 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11155,6 +11327,7 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool { return true } // match: (SUBSshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftRA x y [c]) for { x := v_0 @@ -11163,6 +11336,9 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11229,6 +11405,7 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool { return true } // match: (SUBSshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBSshiftRL x y [c]) for { x := v_0 @@ -11237,6 +11414,9 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBSshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11432,6 +11612,7 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool { return true } // match: (SUBshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftLL x y [c]) for { x := v_0 @@ -11440,6 +11621,9 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11523,6 +11707,7 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool { return true } // match: (SUBshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftRA x y [c]) for { x := v_0 @@ -11531,6 +11716,9 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11614,6 +11802,7 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool { return true } // match: (SUBshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (SUBshiftRL x y [c]) for { x := v_0 @@ -11622,6 +11811,9 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMSUBshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11824,6 +12016,7 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool { return true } // match: (TEQshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftLL x y [c]) for { x := v_0 @@ -11832,6 +12025,9 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11898,6 +12094,7 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool { return true } // match: (TEQshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftRA x y [c]) for { x := v_0 @@ -11906,6 +12103,9 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -11972,6 +12172,7 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool { return true } // match: (TEQshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TEQshiftRL x y [c]) for { x := v_0 @@ -11980,6 +12181,9 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTEQshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12182,6 +12386,7 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool { return true } // match: (TSTshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftLL x y [c]) for { x := v_0 @@ -12190,6 +12395,9 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12256,6 +12464,7 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool { return true } // match: (TSTshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftRA x y [c]) for { x := v_0 @@ -12264,6 +12473,9 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12330,6 +12542,7 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool { return true } // match: (TSTshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (TSTshiftRL x y [c]) for { x := v_0 @@ -12338,6 +12551,9 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMTSTshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12659,6 +12875,7 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool { return true } // match: (XORshiftLLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftLL x y [c]) for { x := v_0 @@ -12667,6 +12884,9 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftLL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12750,6 +12970,7 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool { return true } // match: (XORshiftRAreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftRA x y [c]) for { x := v_0 @@ -12758,6 +12979,9 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftRA) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) @@ -12857,6 +13081,7 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool { return true } // match: (XORshiftRLreg x y (MOVWconst [c])) + // cond: 0 <= c && c < 32 // result: (XORshiftRL x y [c]) for { x := v_0 @@ -12865,6 +13090,9 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool { break } c := auxIntToInt32(v_2.AuxInt) + if !(0 <= c && c < 32) { + break + } v.reset(OpARMXORshiftRL) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, y) diff --git a/test/fixedbugs/issue48476.go b/test/fixedbugs/issue48476.go new file mode 100644 index 0000000000..6b77f7c3c6 --- /dev/null +++ b/test/fixedbugs/issue48476.go @@ -0,0 +1,21 @@ +// run + +// Copyright 2021 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 main + +import "fmt" + +//go:noinline +func f(x uint64) uint64 { + s := "\x04" + c := s[0] + return x << c << 4 +} +func main() { + if want, got := uint64(1<<8), f(1); want != got { + panic(fmt.Sprintf("want %x got %x", want, got)) + } +} -- cgit v1.2.3-54-g00ecf From 79159f2e83662dac4089e2f5231ec23de9da10b9 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Sun, 19 Sep 2021 14:20:56 -0700 Subject: cmd/compile: fix simplification rules on arm/arm64 Fixes #48473 Change-Id: Ic1e918f916eae223a3b530a51a58f03031924670 Reviewed-on: https://go-review.googlesource.com/c/go/+/350913 Trust: Keith Randall Run-TryBot: Keith Randall TryBot-Result: Go Bot Reviewed-by: Cherry Mui --- src/cmd/compile/internal/ssa/gen/ARM.rules | 36 +-- src/cmd/compile/internal/ssa/gen/ARM64.rules | 54 ++--- src/cmd/compile/internal/ssa/rewriteARM.go | 228 ++++++++---------- src/cmd/compile/internal/ssa/rewriteARM64.go | 340 +++++++++++---------------- test/fixedbugs/issue48473.go | 30 +++ 5 files changed, 314 insertions(+), 374 deletions(-) create mode 100644 test/fixedbugs/issue48473.go diff --git a/src/cmd/compile/internal/ssa/gen/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules index a6686237be..2bc58a3c47 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM.rules @@ -1239,24 +1239,24 @@ (AND x (MVN y)) => (BIC x y) // simplification with *shift ops -(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(RSBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0]) -(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0]) +(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(RSBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(ANDshiftLL y:(SLLconst x [c]) x [c]) => y +(ANDshiftRL y:(SRLconst x [c]) x [c]) => y +(ANDshiftRA y:(SRAconst x [c]) x [c]) => y +(ORshiftLL y:(SLLconst x [c]) x [c]) => y +(ORshiftRL y:(SRLconst x [c]) x [c]) => y +(ORshiftRA y:(SRAconst x [c]) x [c]) => y +(XORshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(XORshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(XORshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0]) +(BICshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0]) (AND x (MVNshiftLL y [c])) => (BICshiftLL x y [c]) (AND x (MVNshiftRL y [c])) => (BICshiftRL x y [c]) (AND x (MVNshiftRA y [c])) => (BICshiftRA x y [c]) diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index b722d1cf1c..4b66883f26 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -1723,33 +1723,33 @@ (TSTshiftRO x (MOVDconst [c]) [d]) => (TSTconst x [rotateRight64(c, d)]) // simplification with *shift ops -(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(ANDshiftRO x y:(RORconst x [c]) [d]) && c==d => y -(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y -(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y -(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y -(ORshiftRO x y:(RORconst x [c]) [d]) && c==d => y -(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(XORshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0]) -(BICshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [0]) -(EONshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(EONshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(EONshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(EONshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1]) -(ORNshiftRO x (RORconst x [c]) [d]) && c==d => (MOVDconst [-1]) +(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(ANDshiftLL y:(SLLconst x [c]) x [c]) => y +(ANDshiftRL y:(SRLconst x [c]) x [c]) => y +(ANDshiftRA y:(SRAconst x [c]) x [c]) => y +(ANDshiftRO y:(RORconst x [c]) x [c]) => y +(ORshiftLL y:(SLLconst x [c]) x [c]) => y +(ORshiftRL y:(SRLconst x [c]) x [c]) => y +(ORshiftRA y:(SRAconst x [c]) x [c]) => y +(ORshiftRO y:(RORconst x [c]) x [c]) => y +(XORshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(XORshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0]) +(BICshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0]) +(EONshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1]) +(EONshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1]) +(ORNshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1]) // Generate rotates with const shift (ADDshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x) diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index 2c536e1a52..496f9b4ae2 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -2655,18 +2655,16 @@ func rewriteValueARM_OpARMANDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2750,18 +2748,16 @@ func rewriteValueARM_OpARMANDshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2845,18 +2841,16 @@ func rewriteValueARM_OpARMANDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -3144,17 +3138,15 @@ func rewriteValueARM_OpARMBICshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -3204,17 +3196,15 @@ func rewriteValueARM_OpARMBICshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -3264,17 +3254,15 @@ func rewriteValueARM_OpARMBICshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -8657,18 +8645,16 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -8752,18 +8738,16 @@ func rewriteValueARM_OpARMORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -8863,18 +8847,16 @@ func rewriteValueARM_OpARMORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + y := v_0 + if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c { break } - c := auxIntToInt32(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -9471,17 +9453,15 @@ func rewriteValueARM_OpARMRSBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -9566,17 +9546,15 @@ func rewriteValueARM_OpARMRSBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -9661,17 +9639,15 @@ func rewriteValueARM_OpARMRSBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (RSBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (RSBshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11571,17 +11547,15 @@ func rewriteValueARM_OpARMSUBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11666,17 +11640,15 @@ func rewriteValueARM_OpARMSUBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -11761,17 +11733,15 @@ func rewriteValueARM_OpARMSUBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -12834,17 +12804,15 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftLL (SLLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSLLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -12929,17 +12897,15 @@ func rewriteValueARM_OpARMXORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRA (SRAconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRAconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) @@ -13040,17 +13006,15 @@ func rewriteValueARM_OpARMXORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRL (SRLconst x [c]) x [c]) // result: (MOVWconst [0]) for { - d := auxIntToInt32(v.AuxInt) - x := v_0 - if v_1.Op != OpARMSRLconst { + c := auxIntToInt32(v.AuxInt) + if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c { break } - c := auxIntToInt32(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARMMOVWconst) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 06cc5a62b2..614e71f852 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -2314,18 +2314,16 @@ func rewriteValueARM64_OpARM64ANDshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2368,18 +2366,16 @@ func rewriteValueARM64_OpARM64ANDshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2422,18 +2418,16 @@ func rewriteValueARM64_OpARM64ANDshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2476,18 +2470,16 @@ func rewriteValueARM64_OpARM64ANDshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (ANDshiftRO x y:(RORconst x [c]) [d]) - // cond: c==d + // match: (ANDshiftRO y:(RORconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -2617,17 +2609,15 @@ func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -2653,17 +2643,15 @@ func rewriteValueARM64_OpARM64BICshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -2689,17 +2677,15 @@ func rewriteValueARM64_OpARM64BICshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -2725,17 +2711,15 @@ func rewriteValueARM64_OpARM64BICshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (BICshiftRO x (RORconst x [c]) [d]) - // cond: c==d + // match: (BICshiftRO (RORconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4122,17 +4106,15 @@ func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (EONshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4158,17 +4140,15 @@ func rewriteValueARM64_OpARM64EONshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (EONshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4194,17 +4174,15 @@ func rewriteValueARM64_OpARM64EONshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (EONshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -4230,17 +4208,15 @@ func rewriteValueARM64_OpARM64EONshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (EONshiftRO x (RORconst x [c]) [d]) - // cond: c==d + // match: (EONshiftRO (RORconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18219,17 +18195,15 @@ func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18255,17 +18229,15 @@ func rewriteValueARM64_OpARM64ORNshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18291,17 +18263,15 @@ func rewriteValueARM64_OpARM64ORNshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18327,17 +18297,15 @@ func rewriteValueARM64_OpARM64ORNshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (ORNshiftRO x (RORconst x [c]) [d]) - // cond: c==d + // match: (ORNshiftRO (RORconst x [c]) x [c]) // result: (MOVDconst [-1]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -18450,18 +18418,16 @@ func rewriteValueARM64_OpARM64ORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftLL x y:(SLLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftLL y:(SLLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -20130,18 +20096,16 @@ func rewriteValueARM64_OpARM64ORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRA x y:(SRAconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRA y:(SRAconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -20184,18 +20148,16 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRL x y:(SRLconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRL y:(SRLconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -20318,18 +20280,16 @@ func rewriteValueARM64_OpARM64ORshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (ORshiftRO x y:(RORconst x [c]) [d]) - // cond: c==d + // match: (ORshiftRO y:(RORconst x [c]) x [c]) // result: y for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - y := v_1 - if y.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + y := v_0 + if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c { break } - c := auxIntToInt64(y.AuxInt) - if x != y.Args[0] || !(c == d) { + x := y.Args[0] + if x != v_1 { break } v.copyOf(y) @@ -21389,17 +21349,15 @@ func rewriteValueARM64_OpARM64SUBshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -21425,17 +21383,15 @@ func rewriteValueARM64_OpARM64SUBshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -21461,17 +21417,15 @@ func rewriteValueARM64_OpARM64SUBshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (SUBshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (SUBshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -22641,17 +22595,15 @@ func rewriteValueARM64_OpARM64XORshiftLL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftLL x (SLLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftLL (SLLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SLLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -22853,17 +22805,15 @@ func rewriteValueARM64_OpARM64XORshiftRA(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRA x (SRAconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRA (SRAconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRAconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -22907,17 +22857,15 @@ func rewriteValueARM64_OpARM64XORshiftRL(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRL x (SRLconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRL (SRLconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64SRLconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) @@ -22995,17 +22943,15 @@ func rewriteValueARM64_OpARM64XORshiftRO(v *Value) bool { v.AddArg(x) return true } - // match: (XORshiftRO x (RORconst x [c]) [d]) - // cond: c==d + // match: (XORshiftRO (RORconst x [c]) x [c]) // result: (MOVDconst [0]) for { - d := auxIntToInt64(v.AuxInt) - x := v_0 - if v_1.Op != OpARM64RORconst { + c := auxIntToInt64(v.AuxInt) + if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c { break } - c := auxIntToInt64(v_1.AuxInt) - if x != v_1.Args[0] || !(c == d) { + x := v_0.Args[0] + if x != v_1 { break } v.reset(OpARM64MOVDconst) diff --git a/test/fixedbugs/issue48473.go b/test/fixedbugs/issue48473.go new file mode 100644 index 0000000000..8edef1f14f --- /dev/null +++ b/test/fixedbugs/issue48473.go @@ -0,0 +1,30 @@ +// run + +// Copyright 2021 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 main + +import "fmt" + +func f(x uint64) uint64 { + s := "\x04" + c := s[0] + return x - x< Date: Tue, 17 Aug 2021 07:24:31 -0500 Subject: cmd/compile: document register-based ABI for ppc64 This adds the ppc64 information to the ABI doc. Change-Id: I55ce77a59a8b50309d02e3e0de0cea3fc62ad003 Reviewed-on: https://go-review.googlesource.com/c/go/+/342869 Trust: Lynn Boger Reviewed-by: Cherry Mui --- src/cmd/compile/abi-internal.md | 99 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md index 3619aea4aa..50d8ed9159 100644 --- a/src/cmd/compile/abi-internal.md +++ b/src/cmd/compile/abi-internal.md @@ -627,6 +627,105 @@ modifying or saving the FPCR. Functions are allowed to modify it between calls (as long as they restore it), but as of this writing Go code never does. +### ppc64 architecture + +The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments +and results. + +It uses F1 – F12 for floating-point arguments and results. + +Register R31 is a permanent scratch register in Go. + +Special-purpose registers used within Go generated code and Go +assembly code are as follows: + +| Register | Call meaning | Return meaning | Body meaning | +| --- | --- | --- | --- | +| R0 | Zero value | Same | Same | +| R1 | Stack pointer | Same | Same | +| R2 | TOC register | Same | Same | +| R11 | Closure context pointer | Scratch | Scratch | +| R12 | Function address on indirect calls | Scratch | Scratch | +| R13 | TLS pointer | Same | Same | +| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero | +| R30 | Current goroutine | Same | Same | +| R31 | Scratch | Scratch | Scratch | +| LR | Link register | Link register | Scratch | +*Rationale*: These register meanings are compatible with Go’s +stack-based calling convention. + +The link register, LR, holds the function return +address at the function entry and is set to the correct return +address before exiting the function. It is also used +in some cases as the function address when doing an indirect call. + +The register R2 contains the address of the TOC (table of contents) which +contains data or code addresses used when generating position independent +code. Non-Go code generated when using cgo contains TOC-relative addresses +which depend on R2 holding a valid TOC. Go code compiled with -shared or +-dynlink initializes and maintains R2 and uses it in some cases for +function calls; Go code compiled without these options does not modify R2. + +When making a function call R12 contains the function address for use by the +code to generate R2 at the beginning of the function. R12 can be used for +other purposes within the body of the function, such as trampoline generation. + +R20 and R21 are used in duffcopy and duffzero which could be generated +before arguments are saved so should not be used for register arguments. + +The Count register CTR can be used as the call target for some branch instructions. +It holds the return address when preemption has occurred. + +On PPC64 when a float32 is loaded it becomes a float64 in the register, which is +different from other platforms and that needs to be recognized by the internal +implementation of reflection so that float32 arguments are passed correctly. + +Registers R18 - R29 and F13 - F31 are considered scratch registers. + +#### Stack layout + +The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed +to 16 bytes when calling cgo. + +A function's stack frame, after the frame is created, is laid out as +follows: + + +------------------------------+ + | ... locals ... | + | ... outgoing arguments ... | + | 24 TOC register R2 save | When compiled with -shared/-dynlink + | 16 Unused in Go | Not used in Go + | 8 CR save | nonvolatile CR fields + | 0 return PC | ← R1 points to + +------------------------------+ ↓ lower addresses + +The "return PC" is loaded to the link register, LR, as part of the +ppc64 `BL` operations. + +On entry to a non-leaf function, the stack frame size is subtracted from R1 to +create its stack frame, and saves the value of LR at the bottom of the frame. + +A leaf function that does not require any stack space does not modify R1 and +does not save LR. + +*NOTE*: We might need to save the frame pointer on the stack as +in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers +and profilers. + +This stack layout is used by both register-based (ABIInternal) and +stack-based (ABI0) calling conventions. + +#### Flags + +The condition register consists of 8 condition code register fields +CR0-CR7. Go generated code only sets and uses CR0, commonly set by +compare functions and use to determine the target of a conditional +branch. The generated code does not set or use CR1-CR7. + +The floating point status and control register (FPSCR) is initialized +to 0 by the kernel at startup of the Go program and not changed by +the Go generated code. + ## Future directions ### Spill path improvements -- cgit v1.2.3-54-g00ecf From b6dddaccd7e8c9c6768943918d03f455385294b9 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sun, 19 Sep 2021 17:17:31 -0700 Subject: cmd/compile: fix transform.AssignOp to deal with tricky case When going to dictionary formats derived from the function instantiations, I had broken out noder.Assignop() to deal specially with shape types, but didn't quite get the tricky case right. We still need to allow conversion between shape types, but if the destination is an interface, we need to use CONVIFACE rather than CONVNOP. Fixes #48453. Change-Id: I8c4b39c2e628172ac34f493f1dd682cbac1e55ae Reviewed-on: https://go-review.googlesource.com/c/go/+/350949 Trust: Dan Scales Run-TryBot: Dan Scales Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/transform.go | 15 ++++++++++++--- test/typeparam/issue48453.go | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/typeparam/issue48453.go diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index a117e310ea..3e5cfacb97 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -431,9 +431,18 @@ func Assignop(src, dst *types.Type) (ir.Op, string) { return ir.OXXX, "" } - // 1. src type is identical to dst. - if types.IdenticalStrict(src, dst) { - return ir.OCONVNOP, "" + // 1. src type is identical to dst (taking shapes into account) + if types.Identical(src, dst) { + // We already know from assignconvfn above that IdenticalStrict(src, + // dst) is false, so the types are not exactly the same and one of + // src or dst is a shape. If dst is an interface (which means src is + // an interface too), we need a real OCONVIFACE op; otherwise we need a + // OCONVNOP. See issue #48453. + if dst.IsInterface() { + return ir.OCONVIFACE, "" + } else { + return ir.OCONVNOP, "" + } } return typecheck.Assignop1(src, dst) } diff --git a/test/typeparam/issue48453.go b/test/typeparam/issue48453.go new file mode 100644 index 0000000000..0f751d38ed --- /dev/null +++ b/test/typeparam/issue48453.go @@ -0,0 +1,21 @@ +// run -gcflags=-G=3 + +// Copyright 2021 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 main + +//go:noinline +func CopyMap[M interface{ ~map[K]V }, K comparable, V any](m M) M { + out := make(M, len(m)) + for k, v := range m { + out[k] = v + } + return out +} + +func main() { + var m map[*string]int + CopyMap(m) +} -- cgit v1.2.3-54-g00ecf From 988f18d61d27f75f6a21cef0aa28b8d85982a85d Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Mon, 20 Sep 2021 13:28:25 -0400 Subject: go/types: export Named._Orig as Named.Origin Export the _Orig method for Named types, and rename to Origin. As discussed in #47916, Orig is easily confused with Original, which is not as good a name. Spelling out 'Origin' resolves the ambiguity. Updates #47916 Change-Id: I377c73de82310d3f6aa0bd82dddcd60ee067155b Reviewed-on: https://go-review.googlesource.com/c/go/+/350996 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/named.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/go/types/named.go b/src/go/types/named.go index 4a263410fc..302e43174e 100644 --- a/src/go/types/named.go +++ b/src/go/types/named.go @@ -91,9 +91,9 @@ func (t *Named) Obj() *TypeName { return t.orig.obj // for non-instances this is the same as t.obj } -// _Orig returns the original generic type an instantiated type is derived from. -// If t is not an instantiated type, the result is t. -func (t *Named) _Orig() *Named { return t.orig } +// Origin returns the parameterized type from which the named type t is +// instantiated. If t is not an instantiated type, the result is t. +func (t *Named) Origin() *Named { return t.orig } // TODO(gri) Come up with a better representation and API to distinguish // between parameterized instantiated and non-instantiated types. -- cgit v1.2.3-54-g00ecf From 6acac8b6856b2531f4ac7ee0eb37048d588d98c7 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Mon, 6 Sep 2021 17:46:50 -0700 Subject: cmd/compile: delay all transforms for generic funcs/methods This change cleans up the code, by just delaying all transforms on generic function methods/functions until stenciling time. That way, we don't have extra code to decide whether to delay, or an extra value for the typecheck flag. We are already doing all possible transforms at stencil time anyway, so no changes to the stenciling code. transform.go includes a change for one case where we check for shape rather than tparam, now that we only apply transforms to stenciled functions, not generic functions. This change is to allow CONVIFACE node to be correctly inserted (needed for dictionaries), even with this strange code that doesn't add the CONVIFACE node if the concrete type is NOT huge... Change-Id: I5f1e71fab11b53385902074915b3ad85f8e753fa Reviewed-on: https://go-review.googlesource.com/c/go/+/350736 Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall Trust: Dan Scales --- src/cmd/compile/internal/ir/mini.go | 2 +- src/cmd/compile/internal/noder/helpers.go | 60 +++++++++--------------- src/cmd/compile/internal/noder/stmt.go | 71 ++++++----------------------- src/cmd/compile/internal/noder/transform.go | 2 +- 4 files changed, 37 insertions(+), 98 deletions(-) diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go index a7ff4ac9c7..eeb74081fb 100644 --- a/src/cmd/compile/internal/ir/mini.go +++ b/src/cmd/compile/internal/ir/mini.go @@ -62,7 +62,7 @@ const ( func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) } func (n *miniNode) SetTypecheck(x uint8) { - if x > 3 { + if x > 2 { panic(fmt.Sprintf("cannot SetTypecheck %d", x)) } n.bits.set2(miniTypecheckShift, x) diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go index f06dd8b065..636b5d64cd 100644 --- a/src/cmd/compile/internal/noder/helpers.go +++ b/src/cmd/compile/internal/noder/helpers.go @@ -95,16 +95,12 @@ func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node { return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y)) case ir.OADD: n := ir.NewBinaryExpr(pos, op, x, y) - if x.Type().HasTParam() || y.Type().HasTParam() { - // Delay transformAdd() if either arg has a type param, - // since it needs to know the exact types to decide whether - // to transform OADD to OADDSTR. - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - return transformAdd(n) + r := ir.Node(n) + if !delayTransform() { + r = transformAdd(n) + } + return r default: return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y)) } @@ -201,22 +197,10 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node { n := ir.NewBinaryExpr(pos, op, x, y) - if x.Type().HasTParam() || y.Type().HasTParam() { - xIsInt := x.Type().IsInterface() - yIsInt := y.Type().IsInterface() - if !(xIsInt && !yIsInt || !xIsInt && yIsInt) { - // If either arg is a type param, then we can still do the - // transformCompare() if we know that one arg is an interface - // and the other is not. Otherwise, we delay - // transformCompare(), since it needs to know the exact types - // to decide on any needed conversions. - n.SetType(typ) - n.SetTypecheck(3) - return n - } - } typed(typ, n) - transformCompare(n) + if !delayTransform() { + transformCompare(n) + } return n } @@ -288,15 +272,11 @@ func method(typ *types.Type, index int) *types.Field { func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node { n := ir.NewIndexExpr(pos, x, index) - if x.Type().HasTParam() { - // transformIndex needs to know exact type - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - // transformIndex will modify n.Type() for OINDEXMAP. - transformIndex(n) + if !delayTransform() { + // transformIndex will modify n.Type() for OINDEXMAP. + transformIndex(n) + } return n } @@ -306,14 +286,10 @@ func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node { op = ir.OSLICE3 } n := ir.NewSliceExpr(pos, op, x, low, high, max) - if x.Type().HasTParam() { - // transformSlice needs to know if x.Type() is a string or an array or a slice. - n.SetType(typ) - n.SetTypecheck(3) - return n - } typed(typ, n) - transformSlice(n) + if !delayTransform() { + transformSlice(n) + } return n } @@ -355,3 +331,9 @@ func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt { } return ir.NewAssignOpStmt(pos, op, x, bl) } + +// delayTransform returns true if we should delay all transforms, because we are +// creating the nodes for a generic function/method. +func delayTransform() bool { + return ir.CurFunc != nil && ir.CurFunc.Type().HasTParam() +} diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index aefd9fcdaa..805a4710c4 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -40,13 +40,9 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { return wrapname(g.pos(stmt.X), g.expr(stmt.X)) case *syntax.SendStmt: n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value)) - if n.Chan.Type().HasTParam() || n.Value.Type().HasTParam() { - // Delay transforming the send if the channel or value - // have a type param. - n.SetTypecheck(3) - return n + if !delayTransform() { + transformSend(n) } - transformSend(n) n.SetTypecheck(1) return n case *syntax.DeclStmt: @@ -66,11 +62,9 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { lhs := g.expr(stmt.Lhs) n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs) } - if n.X.Typecheck() == 3 { - n.SetTypecheck(3) - return n + if !delayTransform() { + transformAsOp(n) } - transformAsOp(n) n.SetTypecheck(1) return n } @@ -79,46 +73,24 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { rhs := g.exprList(stmt.Rhs) names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def) - // We must delay transforming the assign statement if any of the - // lhs or rhs nodes are also delayed, since transformAssign needs - // to know the types of the left and right sides in various cases. - delay := false - for _, e := range lhs { - if e.Type().HasTParam() || e.Typecheck() == 3 { - delay = true - break - } - } - for _, e := range rhs { - if e.Type().HasTParam() || e.Typecheck() == 3 { - delay = true - break - } - } - if len(lhs) == 1 && len(rhs) == 1 { n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0]) n.Def = initDefn(n, names) - if delay { - n.SetTypecheck(3) - return n + if !delayTransform() { + lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} + transformAssign(n, lhs, rhs) + n.X, n.Y = lhs[0], rhs[0] } - - lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y} - transformAssign(n, lhs, rhs) - n.X, n.Y = lhs[0], rhs[0] n.SetTypecheck(1) return n } n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs) n.Def = initDefn(n, names) - if delay { - n.SetTypecheck(3) - return n + if !delayTransform() { + transformAssign(n, n.Lhs, n.Rhs) } - transformAssign(n, n.Lhs, n.Rhs) n.SetTypecheck(1) return n @@ -128,15 +100,9 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call)) case *syntax.ReturnStmt: n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results)) - for _, e := range n.Results { - if e.Type().HasTParam() { - // Delay transforming the return statement if any of the - // return values have a type param. - n.SetTypecheck(3) - return n - } + if !delayTransform() { + transformReturn(n) } - transformReturn(n) n.SetTypecheck(1) return n case *syntax.IfStmt: @@ -146,19 +112,10 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { case *syntax.SelectStmt: n := g.selectStmt(stmt) - delay := false - for _, ncase := range n.(*ir.SelectStmt).Cases { - if ncase.Comm != nil && ncase.Comm.Typecheck() == 3 { - delay = true - break - } - } - if delay { - n.SetTypecheck(3) - } else { + if !delayTransform() { transformSelect(n.(*ir.SelectStmt)) - n.SetTypecheck(1) } + n.SetTypecheck(1) return n case *syntax.SwitchStmt: return g.switchStmt(stmt) diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index 3e5cfacb97..953036eb42 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -195,7 +195,7 @@ func transformCompare(n *ir.BinaryExpr) { aop, _ := typecheck.Assignop(lt, rt) if aop != ir.OXXX { types.CalcSize(lt) - if lt.HasTParam() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 { + if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 { l = ir.NewConvExpr(base.Pos, aop, rt, l) l.SetTypecheck(1) } -- cgit v1.2.3-54-g00ecf From 6268468e024ce7fa063611b98a2f11f17fd4bad8 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Fri, 3 Sep 2021 17:00:41 +0200 Subject: cmd/link: generate DIE for types referenced only through dictionaries Generate debug_info entries for types that are only referenced through dictionaries. Change-Id: Ic36c2e6d9588ec6746793bb213c2dc0e17a8a850 Reviewed-on: https://go-review.googlesource.com/c/go/+/350532 Run-TryBot: Alessandro Arzilli TryBot-Result: Go Bot Reviewed-by: Dan Scales Reviewed-by: Than McIntosh Trust: Dan Scales Trust: David Chase --- src/cmd/compile/internal/noder/stencil.go | 1 + src/cmd/internal/goobj/objfile.go | 2 ++ src/cmd/internal/obj/objfile.go | 3 +++ src/cmd/link/internal/ld/dwarf.go | 16 +++++++++++++++- src/cmd/link/internal/ld/dwarf_test.go | 23 +++++++++++++++++++++-- src/cmd/link/internal/loader/loader.go | 9 +++++++++ 6 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index e2525a8f7e..7fca674132 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -1479,6 +1479,7 @@ func markTypeUsed(t *types.Type, lsym *obj.LSym) { } else { // TODO: This is somewhat overkill, we really only need it // for types that are put into interfaces. + // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go reflectdata.MarkTypeUsedInInterface(t, lsym) } } diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go index e2858bd57d..20bf0eba89 100644 --- a/src/cmd/internal/goobj/objfile.go +++ b/src/cmd/internal/goobj/objfile.go @@ -304,6 +304,7 @@ const ( const ( SymFlagUsedInIface = 1 << iota SymFlagItab + SymFlagDict ) // Returns the length of the name of the symbol. @@ -333,6 +334,7 @@ func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 } func (s *Sym) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 } func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 } func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 } +func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 } func (s *Sym) SetName(x string, w *Writer) { binary.LittleEndian.PutUint32(s[:], uint32(len(x))) diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 01466ea736..910e6ef0d9 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -340,6 +340,9 @@ func (w *writer) Sym(s *LSym) { if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA { flag2 |= goobj.SymFlagItab } + if strings.HasPrefix(s.Name, w.ctxt.Pkgpath) && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath):], "..dict") { + flag2 |= goobj.SymFlagDict + } name := s.Name if strings.HasPrefix(name, "gofile..") { name = filepath.ToSlash(name) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 839609339f..d72846a691 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1890,6 +1890,8 @@ func dwarfGenerateDebugInfo(ctxt *Link) { // global variables. For each global of this sort, locate // the corresponding compiler-generated DIE symbol and tack // it onto the list associated with the unit. + // Also looks for dictionary symbols and generates DIE symbols for each + // type they reference. for idx := loader.Sym(1); idx < loader.Sym(d.ldr.NDef()); idx++ { if !d.ldr.AttrReachable(idx) || d.ldr.AttrNotInSymbolTable(idx) || @@ -1903,9 +1905,21 @@ func dwarfGenerateDebugInfo(ctxt *Link) { default: continue } - // Skip things with no type + // Skip things with no type, unless it's a dictionary gt := d.ldr.SymGoType(idx) if gt == 0 { + if t == sym.SRODATA { + if d.ldr.IsDict(idx) { + // This is a dictionary, make sure that all types referenced by this dictionary are reachable + relocs := d.ldr.Relocs(idx) + for i := 0; i < relocs.Count(); i++ { + reloc := relocs.At(i) + if reloc.Type() == objabi.R_USEIFACE { + d.defgotype(reloc.Sym()) + } + } + } + } continue } // Skip file local symbols (this includes static tmps, stack diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go index 3d112d97a4..db9002491e 100644 --- a/src/cmd/link/internal/ld/dwarf_test.go +++ b/src/cmd/link/internal/ld/dwarf_test.go @@ -1749,7 +1749,9 @@ func main() { } func TestDictIndex(t *testing.T) { - // Check that variables with a parametric type have a dictionary index attribute + // Check that variables with a parametric type have a dictionary index + // attribute and that types that are only referenced through dictionaries + // have DIEs. testenv.MustHaveGoBuild(t) if runtime.GOOS == "plan9" { @@ -1765,6 +1767,8 @@ package main import "fmt" +type CustomInt int + func testfn[T any](arg T) { var mapvar = make(map[int]T) mapvar[0] = arg @@ -1772,7 +1776,7 @@ func testfn[T any](arg T) { } func main() { - testfn("test") + testfn(CustomInt(3)) } ` @@ -1829,4 +1833,19 @@ func main() { t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index)) } } + + rdr.Seek(0) + ex := examiner{} + if err := ex.populate(rdr); err != nil { + t.Fatalf("error reading DWARF: %v", err) + } + for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} { + dies := ex.Named(typeName) + if len(dies) != 1 { + t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies)) + } + if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 { + t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName) + } + } } diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index f144e00f37..b9a1da6f45 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -1209,6 +1209,15 @@ func (l *Loader) IsItab(i Sym) bool { return r.Sym(li).IsItab() } +// Returns whether this symbol is a dictionary symbol. +func (l *Loader) IsDict(i Sym) bool { + if l.IsExternal(i) { + return false + } + r, li := l.toLocal(i) + return r.Sym(li).IsDict() +} + // Return whether this is a trampoline of a deferreturn call. func (l *Loader) IsDeferReturnTramp(i Sym) bool { return l.deferReturnTramp[i] -- cgit v1.2.3-54-g00ecf From 3c764babe7b5e01a4e04b1a2c7975cdb5c5651a0 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Thu, 9 Sep 2021 09:38:55 -0700 Subject: cmd/go: write go.mod requirements more consistently for go 1.17+ If go.mod declares 1.17 or higher, when the go command rewrites go.mod (for example, after 'go mod tidy'), it will be more consistent about moving requirements in two blocks, one containing only direct requirements, and one containing only indirect requirements. The go command will not move requirements into or out of a block with comments. It may still update versions and "// indirect" comments, and it may delete unneeded requirements though. Fixes #47563 Fixes #47733 Change-Id: Ia6fb3e302be53097893abf01aa7cea60ac7b069a Reviewed-on: https://go-review.googlesource.com/c/go/+/343432 Trust: Jay Conrod Run-TryBot: Jay Conrod Reviewed-by: Bryan C. Mills --- src/cmd/go.mod | 11 +- src/cmd/go.sum | 4 +- src/cmd/go/testdata/script/mod_all.txt | 67 ++++- src/cmd/go/testdata/script/mod_retention.txt | 22 +- .../testdata/script/mod_tidy_compat_ambiguous.txt | 7 +- .../script/mod_tidy_compat_incompatible.txt | 7 +- src/cmd/vendor/golang.org/x/mod/modfile/rule.go | 295 ++++++++++++--------- src/cmd/vendor/modules.txt | 2 +- src/go.mod | 3 + 9 files changed, 266 insertions(+), 152 deletions(-) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 05a118d812..26be677254 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -4,12 +4,15 @@ go 1.18 require ( github.com/google/pprof v0.0.0-20210827144239-02619b876842 - github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 - golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect + golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b golang.org/x/tools v0.1.6-0.20210904010709-360456621443 +) + +require ( + github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect + golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index eebb44c053..19bb1ee213 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -9,8 +9,8 @@ golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1 h1:MwxAfiDvuwX8Nnnc6iRDhzyM golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 h1:7Qds88gNaRx0Dz/1wOwXlR7asekh1B1u26wEwN6FcEI= -golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a h1:55PVa91KndtPGH2lus5l2gDZqoO/x+Oa5CV0lVf8Ij8= +golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/src/cmd/go/testdata/script/mod_all.txt b/src/cmd/go/testdata/script/mod_all.txt index 6fa2d83239..b71a920870 100644 --- a/src/cmd/go/testdata/script/mod_all.txt +++ b/src/cmd/go/testdata/script/mod_all.txt @@ -202,9 +202,9 @@ go mod edit -go=1.17 u/go.mod go mod edit -go=1.17 w/go.mod go mod edit -go=1.17 x/go.mod go mod edit -go=1.17 -cp go.mod go.mod.orig +cmp go.mod go.mod.beforetidy go mod tidy -cmp go.mod go.mod.orig +cmp go.mod go.mod.aftertidy # With lazy loading, 'go list all' with neither -mod=vendor nor -test should # match -mod=vendor without -test in 1.15. @@ -466,3 +466,66 @@ module example.com/x go 1.15 -- x/x.go -- package x +-- go.mod.beforetidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/b v0.1.0 // indirect + example.com/q v0.1.0 + example.com/r v0.1.0 // indirect + example.com/t v0.1.0 + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) +-- go.mod.aftertidy -- +module example.com/main + +// Note: this go.mod file initially specifies go 1.15, +// but includes some redundant roots so that it +// also already obeys the 1.17 lazy loading invariants. +go 1.17 + +require ( + example.com/a v0.1.0 + example.com/q v0.1.0 + example.com/t v0.1.0 +) + +require ( + example.com/b v0.1.0 // indirect + example.com/r v0.1.0 // indirect + example.com/u v0.1.0 // indirect +) + +replace ( + example.com/a v0.1.0 => ./a + example.com/b v0.1.0 => ./b + example.com/c v0.1.0 => ./c + example.com/d v0.1.0 => ./d + example.com/q v0.1.0 => ./q + example.com/r v0.1.0 => ./r + example.com/s v0.1.0 => ./s + example.com/t v0.1.0 => ./t + example.com/u v0.1.0 => ./u + example.com/w v0.1.0 => ./w + example.com/x v0.1.0 => ./x +) diff --git a/src/cmd/go/testdata/script/mod_retention.txt b/src/cmd/go/testdata/script/mod_retention.txt index 481c10d2b7..9d30026459 100644 --- a/src/cmd/go/testdata/script/mod_retention.txt +++ b/src/cmd/go/testdata/script/mod_retention.txt @@ -83,14 +83,14 @@ require ( package x import _ "rsc.io/quote" -- go.mod.crlf -- -module m - -go 1.14 - -require ( - rsc.io/quote v1.5.2 - rsc.io/testonly v1.0.0 // indirect -) +module m + +go 1.14 + +require ( + rsc.io/quote v1.5.2 + rsc.io/testonly v1.0.0 // indirect +) -- go.mod.unsorted -- module m @@ -141,10 +141,10 @@ module m go $goversion +require rsc.io/quote v1.5.2 + require ( - rsc.io/quote v1.5.2 + golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect rsc.io/sampler v1.3.0 // indirect rsc.io/testonly v1.0.0 // indirect ) - -require golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt index 44bc58cc6c..a45de5ad8c 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat_ambiguous.txt @@ -72,10 +72,9 @@ go 1.17 replace example.net/indirect v0.1.0 => ./indirect -require ( - example.net/ambiguous/nested v0.1.0 // indirect - example.net/indirect v0.1.0 -) +require example.net/indirect v0.1.0 + +require example.net/ambiguous/nested v0.1.0 // indirect -- all-m.txt -- example.com/m example.net/ambiguous v0.1.0 diff --git a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt index ea9e42e87e..11313f144c 100644 --- a/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt +++ b/src/cmd/go/testdata/script/mod_tidy_compat_incompatible.txt @@ -97,10 +97,9 @@ replace ( example.net/requireincompatible v0.1.0 => ./requireincompatible ) -require ( - example.com/retract/incompatible v1.0.0 // indirect - example.net/lazy v0.1.0 -) +require example.net/lazy v0.1.0 + +require example.com/retract/incompatible v1.0.0 // indirect -- incompatible.go -- package incompatible diff --git a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go index d6a2d3879e..98211a450a 100644 --- a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go +++ b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go @@ -1034,170 +1034,217 @@ func (f *File) SetRequire(req []*Require) { // SetRequireSeparateIndirect updates the requirements of f to contain the given // requirements. Comment contents (except for 'indirect' markings) are retained -// from the first existing requirement for each module path, and block structure -// is maintained as long as the indirect markings match. +// from the first existing requirement for each module path. Like SetRequire, +// SetRequireSeparateIndirect adds requirements for new paths in req, +// updates the version and "// indirect" comment on existing requirements, +// and deletes requirements on paths not in req. Existing duplicate requirements +// are deleted. // -// Any requirements on paths not already present in the file are added. Direct -// requirements are added to the last block containing *any* other direct -// requirement. Indirect requirements are added to the last block containing -// *only* other indirect requirements. If no suitable block exists, a new one is -// added, with the last block containing a direct dependency (if any) -// immediately before the first block containing only indirect dependencies. +// As its name suggests, SetRequireSeparateIndirect puts direct and indirect +// requirements into two separate blocks, one containing only direct +// requirements, and the other containing only indirect requirements. +// SetRequireSeparateIndirect may move requirements between these two blocks +// when their indirect markings change. However, SetRequireSeparateIndirect +// won't move requirements from other blocks, especially blocks with comments. // -// The Syntax field is ignored for requirements in the given blocks. +// If the file initially has one uncommented block of requirements, +// SetRequireSeparateIndirect will split it into a direct-only and indirect-only +// block. This aids in the transition to separate blocks. func (f *File) SetRequireSeparateIndirect(req []*Require) { - type modKey struct { - path string - indirect bool - } - need := make(map[modKey]string) - for _, r := range req { - need[modKey{r.Mod.Path, r.Indirect}] = r.Mod.Version + // hasComments returns whether a line or block has comments + // other than "indirect". + hasComments := func(c Comments) bool { + return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 || + (len(c.Suffix) == 1 && + strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect") } - comments := make(map[string]Comments) - for _, r := range f.Require { - v, ok := need[modKey{r.Mod.Path, r.Indirect}] - if !ok { - if _, ok := need[modKey{r.Mod.Path, !r.Indirect}]; ok { - if _, dup := comments[r.Mod.Path]; !dup { - comments[r.Mod.Path] = r.Syntax.Comments - } + // moveReq adds r to block. If r was in another block, moveReq deletes + // it from that block and transfers its comments. + moveReq := func(r *Require, block *LineBlock) { + var line *Line + if r.Syntax == nil { + line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}} + r.Syntax = line + if r.Indirect { + r.setIndirect(true) } - r.markRemoved() - continue + } else { + line = new(Line) + *line = *r.Syntax + if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" { + line.Token = line.Token[1:] + } + r.Syntax.Token = nil // Cleanup will delete the old line. + r.Syntax = line } - r.setVersion(v) - delete(need, modKey{r.Mod.Path, r.Indirect}) + line.InBlock = true + block.Line = append(block.Line, line) } + // Examine existing require lines and blocks. var ( - lastDirectOrMixedBlock Expr - firstIndirectOnlyBlock Expr - lastIndirectOnlyBlock Expr + // We may insert new requirements into the last uncommented + // direct-only and indirect-only blocks. We may also move requirements + // to the opposite block if their indirect markings change. + lastDirectIndex = -1 + lastIndirectIndex = -1 + + // If there are no direct-only or indirect-only blocks, a new block may + // be inserted after the last require line or block. + lastRequireIndex = -1 + + // If there's only one require line or block, and it's uncommented, + // we'll move its requirements to the direct-only or indirect-only blocks. + requireLineOrBlockCount = 0 + + // Track the block each requirement belongs to (if any) so we can + // move them later. + lineToBlock = make(map[*Line]*LineBlock) ) - for _, stmt := range f.Syntax.Stmt { + for i, stmt := range f.Syntax.Stmt { switch stmt := stmt.(type) { case *Line: if len(stmt.Token) == 0 || stmt.Token[0] != "require" { continue } - if isIndirect(stmt) { - lastIndirectOnlyBlock = stmt - } else { - lastDirectOrMixedBlock = stmt + lastRequireIndex = i + requireLineOrBlockCount++ + if !hasComments(stmt.Comments) { + if isIndirect(stmt) { + lastIndirectIndex = i + } else { + lastDirectIndex = i + } } + case *LineBlock: if len(stmt.Token) == 0 || stmt.Token[0] != "require" { continue } - indirectOnly := true + lastRequireIndex = i + requireLineOrBlockCount++ + allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) + allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments) for _, line := range stmt.Line { - if len(line.Token) == 0 { - continue - } - if !isIndirect(line) { - indirectOnly = false - break + lineToBlock[line] = stmt + if hasComments(line.Comments) { + allDirect = false + allIndirect = false + } else if isIndirect(line) { + allDirect = false + } else { + allIndirect = false } } - if indirectOnly { - lastIndirectOnlyBlock = stmt - if firstIndirectOnlyBlock == nil { - firstIndirectOnlyBlock = stmt - } - } else { - lastDirectOrMixedBlock = stmt + if allDirect { + lastDirectIndex = i + } + if allIndirect { + lastIndirectIndex = i } } } - isOrContainsStmt := func(stmt Expr, target Expr) bool { - if stmt == target { - return true - } - if stmt, ok := stmt.(*LineBlock); ok { - if target, ok := target.(*Line); ok { - for _, line := range stmt.Line { - if line == target { - return true - } - } + oneFlatUncommentedBlock := requireLineOrBlockCount == 1 && + !hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment()) + + // Create direct and indirect blocks if needed. Convert lines into blocks + // if needed. If we end up with an empty block or a one-line block, + // Cleanup will delete it or convert it to a line later. + insertBlock := func(i int) *LineBlock { + block := &LineBlock{Token: []string{"require"}} + f.Syntax.Stmt = append(f.Syntax.Stmt, nil) + copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) + f.Syntax.Stmt[i] = block + return block + } + + ensureBlock := func(i int) *LineBlock { + switch stmt := f.Syntax.Stmt[i].(type) { + case *LineBlock: + return stmt + case *Line: + block := &LineBlock{ + Token: []string{"require"}, + Line: []*Line{stmt}, } + stmt.Token = stmt.Token[1:] // remove "require" + stmt.InBlock = true + f.Syntax.Stmt[i] = block + return block + default: + panic(fmt.Sprintf("unexpected statement: %v", stmt)) } - return false } - addRequire := func(path, vers string, indirect bool, comments Comments) { - var line *Line - if indirect { - if lastIndirectOnlyBlock != nil { - line = f.Syntax.addLine(lastIndirectOnlyBlock, "require", path, vers) - } else { - // Add a new require block after the last direct-only or mixed "require" - // block (if any). - // - // (f.Syntax.addLine would add the line to an existing "require" block if - // present, but here the existing "require" blocks are all direct-only, so - // we know we need to add a new block instead.) - line = &Line{Token: []string{"require", path, vers}} - lastIndirectOnlyBlock = line - firstIndirectOnlyBlock = line // only block implies first block - if lastDirectOrMixedBlock == nil { - f.Syntax.Stmt = append(f.Syntax.Stmt, line) - } else { - for i, stmt := range f.Syntax.Stmt { - if isOrContainsStmt(stmt, lastDirectOrMixedBlock) { - f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size - copy(f.Syntax.Stmt[i+2:], f.Syntax.Stmt[i+1:]) // shuffle elements up - f.Syntax.Stmt[i+1] = line - break - } - } - } - } + var lastDirectBlock *LineBlock + if lastDirectIndex < 0 { + if lastIndirectIndex >= 0 { + lastDirectIndex = lastIndirectIndex + lastIndirectIndex++ + } else if lastRequireIndex >= 0 { + lastDirectIndex = lastRequireIndex + 1 } else { - if lastDirectOrMixedBlock != nil { - line = f.Syntax.addLine(lastDirectOrMixedBlock, "require", path, vers) - } else { - // Add a new require block before the first indirect block (if any). - // - // That way if the file initially contains only indirect lines, - // the direct lines still appear before it: we preserve existing - // structure, but only to the extent that that structure already - // reflects the direct/indirect split. - line = &Line{Token: []string{"require", path, vers}} - lastDirectOrMixedBlock = line - if firstIndirectOnlyBlock == nil { - f.Syntax.Stmt = append(f.Syntax.Stmt, line) - } else { - for i, stmt := range f.Syntax.Stmt { - if isOrContainsStmt(stmt, firstIndirectOnlyBlock) { - f.Syntax.Stmt = append(f.Syntax.Stmt, nil) // increase size - copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:]) // shuffle elements up - f.Syntax.Stmt[i] = line - break - } - } - } - } + lastDirectIndex = len(f.Syntax.Stmt) } + lastDirectBlock = insertBlock(lastDirectIndex) + } else { + lastDirectBlock = ensureBlock(lastDirectIndex) + } - line.Comments.Before = commentsAdd(line.Comments.Before, comments.Before) - line.Comments.Suffix = commentsAdd(line.Comments.Suffix, comments.Suffix) + var lastIndirectBlock *LineBlock + if lastIndirectIndex < 0 { + lastIndirectIndex = lastDirectIndex + 1 + lastIndirectBlock = insertBlock(lastIndirectIndex) + } else { + lastIndirectBlock = ensureBlock(lastIndirectIndex) + } - r := &Require{ - Mod: module.Version{Path: path, Version: vers}, - Indirect: indirect, - Syntax: line, + // Delete requirements we don't want anymore. + // Update versions and indirect comments on requirements we want to keep. + // If a requirement is in last{Direct,Indirect}Block with the wrong + // indirect marking after this, or if the requirement is in an single + // uncommented mixed block (oneFlatUncommentedBlock), move it to the + // correct block. + // + // Some blocks may be empty after this. Cleanup will remove them. + need := make(map[string]*Require) + for _, r := range req { + need[r.Mod.Path] = r + } + have := make(map[string]*Require) + for _, r := range f.Require { + path := r.Mod.Path + if need[path] == nil || have[path] != nil { + // Requirement not needed, or duplicate requirement. Delete. + r.markRemoved() + continue + } + have[r.Mod.Path] = r + r.setVersion(need[path].Mod.Version) + r.setIndirect(need[path].Indirect) + if need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) { + moveReq(r, lastIndirectBlock) + } else if !need[path].Indirect && + (oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) { + moveReq(r, lastDirectBlock) } - r.setIndirect(indirect) - f.Require = append(f.Require, r) } - for k, vers := range need { - addRequire(k.path, vers, k.indirect, comments[k.path]) + // Add new requirements. + for path, r := range need { + if have[path] == nil { + if r.Indirect { + moveReq(r, lastIndirectBlock) + } else { + moveReq(r, lastDirectBlock) + } + f.Require = append(f.Require, r) + } } + f.SortBlocks() } diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 966ba1358e..4ff07ab015 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -28,7 +28,7 @@ golang.org/x/arch/x86/x86asm ## explicit; go 1.17 golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519/internal/edwards25519 -# golang.org/x/mod v0.5.1-0.20210830214625-1b1db11ec8f4 +# golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a ## explicit; go 1.17 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile diff --git a/src/go.mod b/src/go.mod index a4a6c4f05d..69e2655e88 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,6 +5,9 @@ go 1.18 require ( golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210825183410-e898025ed96a +) + +require ( golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect golang.org/x/text v0.3.7 // indirect ) -- cgit v1.2.3-54-g00ecf From af72ddfcd7826df9aefb2207b8ac270bb91fea2f Mon Sep 17 00:00:00 2001 From: David Chase Date: Thu, 9 Sep 2021 16:34:02 -0400 Subject: cmd/compile: extend dump-to-file to handle "genssa" (asm) case. Extend the existing dump-to-file to also do assembly output to make it easier to write debug-information tests that check for line-numbering in particular orders. Includes POC test (which is silent w/o -v): go test -v -run TestDebugLines cmd/compile/internal/ssa === RUN TestDebugLines Preserving temporary directory /var/folders/v6/xyzzy/T/debug_lines_test321 About to run (cd /var/folders/v6/xyzzy/T/debug_lines_test321; \ GOSSADIR=/var/folders/v6/xyzzy/T/debug_lines_test321 \ /Users/drchase/work/go/bin/go build -o foo.o \ '-gcflags=-N -l -d=ssa/genssa/dump=sayhi' \ /Users/drchase/work/go/src/cmd/compile/internal/ssa/testdata/sayhi.go ) Saw stmt# 8 for submatch '8' on dump line #7 = ' v107 00005 (+8) MOVQ AX, "".n(SP)' Saw stmt# 9 for submatch '9' on dump line #9 = ' v87 00007 (+9) MOVUPS X15, ""..autotmp_2-32(SP)' Saw stmt# 10 for submatch '10' on dump line #46 = ' v65 00044 (+10) MOVUPS X15, ""..autotmp_2-32(SP)' Saw stmt# 11 for submatch '11' on dump line #83 = ' v131 00081 (+11) MOVQ "".wg+8(SP), AX' --- PASS: TestDebugLines (4.95s) PASS ok cmd/compile/internal/ssa 5.685s Includes a test to ensure that inlining information is printed correctly. Updates #47880. Change-Id: I83b596476a88687d71d5b65dbb94641a576d747e Reviewed-on: https://go-review.googlesource.com/c/go/+/348970 Trust: David Chase Run-TryBot: David Chase TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/ssa/compile.go | 59 ++++-- src/cmd/compile/internal/ssa/debug_lines_test.go | 213 +++++++++++++++++++++ src/cmd/compile/internal/ssa/func.go | 2 +- src/cmd/compile/internal/ssa/print.go | 20 +- .../compile/internal/ssa/testdata/inline-dump.go | 17 ++ src/cmd/compile/internal/ssa/testdata/sayhi.go | 12 ++ src/cmd/compile/internal/ssagen/ssa.go | 55 +++++- src/cmd/internal/obj/util.go | 5 + 8 files changed, 360 insertions(+), 23 deletions(-) create mode 100644 src/cmd/compile/internal/ssa/debug_lines_test.go create mode 100644 src/cmd/compile/internal/ssa/testdata/inline-dump.go create mode 100644 src/cmd/compile/internal/ssa/testdata/sayhi.go diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index cd8eba405d..f87ea5b893 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -10,9 +10,11 @@ import ( "fmt" "hash/crc32" "internal/buildcfg" + "io" "log" "math/rand" "os" + "path/filepath" "regexp" "runtime" "sort" @@ -59,7 +61,7 @@ func Compile(f *Func) { printFunc(f) } f.HTMLWriter.WritePhase("start", "start") - if BuildDump != "" && BuildDump == f.Name { + if BuildDump[f.Name] { f.dumpFile("build") } if checkEnabled { @@ -163,25 +165,37 @@ func Compile(f *Func) { phaseName = "" } -// dumpFile creates a file from the phase name and function name -// Dumping is done to files to avoid buffering huge strings before -// output. -func (f *Func) dumpFile(phaseName string) { +// DumpFileForPhase creates a file from the function name and phase name, +// warning and returning nil if this is not possible. +func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser { f.dumpFileSeq++ fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName) fname = strings.Replace(fname, " ", "_", -1) fname = strings.Replace(fname, "/", "_", -1) fname = strings.Replace(fname, ":", "_", -1) + if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" { + fname = filepath.Join(ssaDir, fname) + } + fi, err := os.Create(fname) if err != nil { f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname) - return + return nil } + return fi +} - p := stringFuncPrinter{w: fi} - fprintFunc(p, f) - fi.Close() +// dumpFile creates a file from the phase name and function name +// Dumping is done to files to avoid buffering huge strings before +// output. +func (f *Func) dumpFile(phaseName string) { + fi := f.DumpFileForPhase(phaseName) + if fi != nil { + p := stringFuncPrinter{w: fi} + fprintFunc(p, f) + fi.Close() + } } type pass struct { @@ -224,7 +238,9 @@ var IntrinsicsDisable bool var BuildDebug int var BuildTest int var BuildStats int -var BuildDump string // name of function to dump after initial build of ssa +var BuildDump map[string]bool = make(map[string]bool) // names of functions to dump after initial build of ssa + +var GenssaDump map[string]bool = make(map[string]bool) // names of functions to dump after ssa has been converted to asm // PhaseOption sets the specified flag in the specified ssa phase, // returning empty string if this was successful or a string explaining @@ -248,7 +264,7 @@ func PhaseOption(phase, flag string, val int, valString string) string { switch phase { case "", "help": lastcr := 0 - phasenames := " check, all, build, intrinsics" + phasenames := " check, all, build, intrinsics, genssa" for _, p := range passes { pn := strings.Replace(p.name, " ", "_", -1) if len(pn)+len(phasenames)-lastcr > 70 { @@ -278,6 +294,7 @@ where: Phase "all" supports flags "time", "mem", and "dump". Phase "intrinsics" supports flags "on", "off", and "debug". +Phase "genssa" (assembly generation) supports the flag "dump". If the "dump" flag is specified, the output is written on a file named ___.dump; otherwise it is directed to stdout. @@ -339,10 +356,11 @@ commas. For example: case "dump": alldump = val != 0 if alldump { - BuildDump = valString + BuildDump[valString] = true + GenssaDump[valString] = true } default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/all/{time,mem,dump=function_name})", flag, phase) } } @@ -355,7 +373,7 @@ commas. For example: case "debug": IntrinsicsDebug = val default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase) } return "" } @@ -368,9 +386,18 @@ commas. For example: case "stats": BuildStats = val case "dump": - BuildDump = valString + BuildDump[valString] = true + default: + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/build/{debug,test,stats,dump=function_name})", flag, phase) + } + return "" + } + if phase == "genssa" { + switch flag { + case "dump": + GenssaDump[valString] = true default: - return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase) + return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase) } return "" } diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go new file mode 100644 index 0000000000..c5a0fe449c --- /dev/null +++ b/src/cmd/compile/internal/ssa/debug_lines_test.go @@ -0,0 +1,213 @@ +// Copyright 2021 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_test + +import ( + "bufio" + "bytes" + "flag" + "runtime" + "sort" + + // "flag" + "fmt" + "internal/testenv" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "strconv" + "testing" +) + +// Matches lines in genssa output that are marked "isstmt", and the parenthesized plus-prefixed line number is a submatch +var asmLine *regexp.Regexp = regexp.MustCompile(`^\s[vb][0-9]+\s+[0-9]+\s\(\+([0-9]+)\)`) + +// this matches e.g. ` v123456789 000007 (+9876654310) MOVUPS X15, ""..autotmp_2-32(SP)` + +// Matches lines in genssa output that describe an inlined file (on a Unix filesystem). Note it expects an unadventurous choice of basename. +var inlineLine *regexp.Regexp = regexp.MustCompile(`^#\s/.*/[-a-zA-Z0-9_]+\.go:([0-9]+)`) + +// this matches e.g. # /pa/inline-dumpxxxx.go:6 + +var testGoArchFlag = flag.String("arch", "", "run test for specified architecture") + +func testGoArch() string { + if *testGoArchFlag == "" { + return runtime.GOARCH + } + return *testGoArchFlag +} + +func TestDebugLines(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938 + } + // This test is potentially fragile, the goal is that debugging should step properly through "sayhi" + // If the blocks are reordered in a way that changes the statement order but execution flows correctly, + // then rearrange the expected numbers. Register abi and not-register-abi also have different sequences, + // at least for now. + + switch testGoArch() { + case "arm64", "amd64": // register ABI + testDebugLines(t, "sayhi.go", "sayhi", []int{8, 9, 10, 11}) + + case "arm", "386": // probably not register ABI for a while + testDebugLines(t, "sayhi.go", "sayhi", []int{9, 10, 11}) + + default: // expect ppc64le and riscv will pick up register ABI soonish, not sure about others + t.Skip("skipped for many architectures, also changes w/ register ABI") + } +} + +func TestInlineLines(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Windows lacks $HOME which complicates workaround for 'missing $GOPATH'") // $HOME needed to work around #43938 + } + if runtime.GOARCH != "amd64" && *testGoArchFlag == "" { + // As of september 2021, works for everything except mips64, but still potentially fragile + t.Skip("only runs for amd64 unless -arch explicitly supplied") + } + + want := [][]int{{3}, {4, 10}, {4, 10, 16}, {4, 10}, {4, 11, 16}, {4, 11}, {4}, {5, 10}, {5, 10, 16}, {5, 10}, {5, 11, 16}, {5, 11}, {5}} + testInlineStack(t, "inline-dump.go", "f", want) +} + +func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte { + testenv.MustHaveGoBuild(t) + + tmpdir, err := ioutil.TempDir("", "debug_lines_test") + if err != nil { + panic(fmt.Sprintf("Problem creating TempDir, error %v", err)) + } + if testing.Verbose() { + fmt.Printf("Preserving temporary directory %s\n", tmpdir) + } else { + defer os.RemoveAll(tmpdir) + } + + source, err := filepath.Abs(filepath.Join("testdata", file)) + if err != nil { + panic(fmt.Sprintf("Could not get abspath of testdata directory and file, %v", err)) + } + + cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "foo.o", "-gcflags=-d=ssa/genssa/dump="+function+" "+moreGCFlags, source) + cmd.Dir = tmpdir + cmd.Env = replaceEnv(cmd.Env, "GOSSADIR", tmpdir) + cmd.Env = replaceEnv(cmd.Env, "HOME", os.Getenv("HOME")) // workaround for #43938 + testGoos := "linux" // default to linux + if testGoArch() == "wasm" { + testGoos = "js" + } + cmd.Env = replaceEnv(cmd.Env, "GOOS", testGoos) + cmd.Env = replaceEnv(cmd.Env, "GOARCH", testGoArch()) + + if testing.Verbose() { + fmt.Printf("About to run %s\n", asCommandLine("", cmd)) + } + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + t.Fatalf("error running cmd %s: %v\nstdout:\n%sstderr:\n%s\n", asCommandLine("", cmd), err, stdout.String(), stderr.String()) + } + + if s := stderr.String(); s != "" { + t.Fatalf("Wanted empty stderr, instead got:\n%s\n", s) + } + + dumpFile := filepath.Join(tmpdir, function+"_01__genssa.dump") + dumpBytes, err := os.ReadFile(dumpFile) + if err != nil { + t.Fatalf("Could not read dump file %s, err=%v", dumpFile, err) + } + return dumpBytes +} + +func sortInlineStacks(x [][]int) { + sort.Slice(x, func(i, j int) bool { + if len(x[i]) != len(x[j]) { + return len(x[i]) < len(x[j]) + } + for k := range x[i] { + if x[i][k] != x[j][k] { + return x[i][k] < x[j][k] + } + } + return false + }) +} + +// testInlineStack ensures that inlining is described properly in the comments in the dump file +func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) { + // this is an inlining reporting test, not an optimization test. -N makes it less fragile + dumpBytes := compileAndDump(t, file, function, "-N") + dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) + dumpLineNum := 0 + var gotStmts []int + var gotStacks [][]int + for dump.Scan() { + line := dump.Text() + dumpLineNum++ + matches := inlineLine.FindStringSubmatch(line) + if len(matches) == 2 { + stmt, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) + } + if testing.Verbose() { + fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) + } + gotStmts = append(gotStmts, int(stmt)) + } else if len(gotStmts) > 0 { + gotStacks = append(gotStacks, gotStmts) + gotStmts = nil + } + } + if len(gotStmts) > 0 { + gotStacks = append(gotStacks, gotStmts) + gotStmts = nil + } + sortInlineStacks(gotStacks) + sortInlineStacks(wantStacks) + if !reflect.DeepEqual(wantStacks, gotStacks) { + t.Errorf("wanted inlines %+v but got %+v", wantStacks, gotStacks) + } + +} + +// testDebugLines compiles testdata/ with flags -N -l and -d=ssa/genssa/dump= +// then verifies that the statement-marked lines in that file are the same as those in wantStmts +// These files must all be short because this is super-fragile. +// "go build" is run in a temporary directory that is normally deleted, unless -test.v +func testDebugLines(t *testing.T, file, function string, wantStmts []int) { + dumpBytes := compileAndDump(t, file, function, "-N -l") + dump := bufio.NewScanner(bytes.NewReader(dumpBytes)) + var gotStmts []int + dumpLineNum := 0 + for dump.Scan() { + line := dump.Text() + dumpLineNum++ + matches := asmLine.FindStringSubmatch(line) + if len(matches) == 2 { + stmt, err := strconv.ParseInt(matches[1], 10, 32) + if err != nil { + t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err) + } + if testing.Verbose() { + fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line) + } + gotStmts = append(gotStmts, int(stmt)) + } + } + if !reflect.DeepEqual(wantStmts, gotStmts) { + t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts) + } + +} diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go index fac876c23e..7728a395e0 100644 --- a/src/cmd/compile/internal/ssa/func.go +++ b/src/cmd/compile/internal/ssa/func.go @@ -43,7 +43,7 @@ type Func struct { logfiles map[string]writeSyncer HTMLWriter *HTMLWriter // html writer, for debugging DebugTest bool // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases - PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. + PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. There's an odd dependence on this in debug.go for method logf. ruleMatches map[string]int // number of times countRule was called during compilation for any given string ABI0 *abi.ABIConfig // A copy, for no-sync access ABI1 *abi.ABIConfig // A copy, for no-sync access diff --git a/src/cmd/compile/internal/ssa/print.go b/src/cmd/compile/internal/ssa/print.go index 81c64a7692..96cd2c7c90 100644 --- a/src/cmd/compile/internal/ssa/print.go +++ b/src/cmd/compile/internal/ssa/print.go @@ -6,6 +6,7 @@ package ssa import ( "bytes" + "cmd/internal/src" "crypto/sha256" "fmt" "io" @@ -83,13 +84,26 @@ func (p stringFuncPrinter) endBlock(b *Block, reachable bool) { fmt.Fprintln(p.w, " "+b.LongString()) } +func StmtString(p src.XPos) string { + linenumber := "(?) " + if p.IsKnown() { + pfx := "" + if p.IsStmt() == src.PosIsStmt { + pfx = "+" + } + if p.IsStmt() == src.PosNotStmt { + pfx = "-" + } + linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line()) + } + return linenumber +} + func (p stringFuncPrinter) value(v *Value, live bool) { if !p.printDead && !live { return } - fmt.Fprint(p.w, " ") - //fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos)) - //fmt.Fprint(p.w, ": ") + fmt.Fprintf(p.w, " %s", StmtString(v.Pos)) fmt.Fprint(p.w, v.LongString()) if !live { fmt.Fprint(p.w, " DEAD") diff --git a/src/cmd/compile/internal/ssa/testdata/inline-dump.go b/src/cmd/compile/internal/ssa/testdata/inline-dump.go new file mode 100644 index 0000000000..97893b6f21 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/inline-dump.go @@ -0,0 +1,17 @@ +package foo + +func f(m, n int) int { + a := g(n) + b := g(m) + return a + b +} + +func g(x int) int { + y := h(x + 1) + z := h(x - 1) + return y + z +} + +func h(x int) int { + return x * x +} diff --git a/src/cmd/compile/internal/ssa/testdata/sayhi.go b/src/cmd/compile/internal/ssa/testdata/sayhi.go new file mode 100644 index 0000000000..680e1eb3a1 --- /dev/null +++ b/src/cmd/compile/internal/ssa/testdata/sayhi.go @@ -0,0 +1,12 @@ +package foo + +import ( + "fmt" + "sync" +) + +func sayhi(n int, wg *sync.WaitGroup) { + fmt.Println("hi", n) + fmt.Println("hi", n) + wg.Done() +} diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 08f36ce7be..82d232f940 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -6703,7 +6703,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) { var progToValue map[*obj.Prog]*ssa.Value var progToBlock map[*obj.Prog]*ssa.Block var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point. - if f.PrintOrHtmlSSA { + gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name] + if gatherPrintInfo { progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues()) progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks()) f.Logf("genssa %s\n", f.Name) @@ -6814,7 +6815,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { valueToProgAfter[v.ID] = s.pp.Next } - if f.PrintOrHtmlSSA { + if gatherPrintInfo { for ; x != s.pp.Next; x = x.Link { progToValue[x] = v } @@ -6844,7 +6845,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) { x := s.pp.Next s.SetPos(b.Pos) Arch.SSAGenBlock(&s, b, next) - if f.PrintOrHtmlSSA { + if gatherPrintInfo { for ; x != s.pp.Next; x = x.Link { progToBlock[x] = b } @@ -7023,6 +7024,54 @@ func genssa(f *ssa.Func, pp *objw.Progs) { buf.WriteString("") f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String()) } + if ssa.GenssaDump[f.Name] { + fi := f.DumpFileForPhase("genssa") + if fi != nil { + + // inliningDiffers if any filename changes or if any line number except the innermost (index 0) changes. + inliningDiffers := func(a, b []src.Pos) bool { + if len(a) != len(b) { + return true + } + for i := range a { + if a[i].Filename() != b[i].Filename() { + return true + } + if i > 0 && a[i].Line() != b[i].Line() { + return true + } + } + return false + } + + var allPosOld []src.Pos + var allPos []src.Pos + + for p := pp.Text; p != nil; p = p.Link { + if p.Pos.IsKnown() { + allPos = p.AllPos(allPos) + if inliningDiffers(allPos, allPosOld) { + for i := len(allPos) - 1; i >= 0; i-- { + pos := allPos[i] + fmt.Fprintf(fi, "# %s:%d\n", pos.Filename(), pos.Line()) + } + allPos, allPosOld = allPosOld, allPos // swap, not copy, so that they do not share slice storage. + } + } + + var s string + if v, ok := progToValue[p]; ok { + s = v.String() + } else if b, ok := progToBlock[p]; ok { + s = b.String() + } else { + s = " " // most value and branch strings are 2-3 characters long + } + fmt.Fprintf(fi, " %-6s\t%.5d %s\t%s\n", s, p.Pc, ssa.StmtString(p.Pos), p.InstructionString()) + } + fi.Close() + } + } defframe(&s, e, f) diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index e8441a6969..0c9dde7965 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -7,6 +7,7 @@ package obj import ( "bytes" "cmd/internal/objabi" + "cmd/internal/src" "fmt" "internal/buildcfg" "io" @@ -47,6 +48,10 @@ func (p *Prog) InnermostFilename() string { return pos.Filename() } +func (p *Prog) AllPos(result []src.Pos) []src.Pos { + return p.Ctxt.AllPos(p.Pos, result) +} + var armCondCode = []string{ ".EQ", ".NE", -- cgit v1.2.3-54-g00ecf