Saves on both space and cost of map operations. Saves about 3% in compile time.
name old time/op new time/op delta
Template 251ms ± 2% 244ms ± 1% -2.78% (p=0.000 n=8+8)
Unicode 149ms ± 5% 135ms ± 2% -9.03% (p=0.000 n=10+10)
GoTypes 1.38s ± 1% 1.35s ± 1% -2.29% (p=0.000 n=10+10)
Compiler 115ms ± 2% 112ms ± 2% -2.50% (p=0.001 n=10+9)
SSA 11.9s ± 0% 11.4s ± 0% -4.04% (p=0.000 n=9+10)
Flate 153ms ± 1% 148ms ± 1% -3.32% (p=0.000 n=10+9)
GoParser 284ms ± 2% 280ms ± 1% -1.70% (p=0.002 n=10+10)
Tar 209ms ± 2% 205ms ± 2% -1.98% (p=0.004 n=9+10)
XML 287ms ± 2% 281ms ± 1% -2.06% (p=0.000 n=10+10)
LinkCompiler 508ms ± 2% 501ms ± 2% -1.31% (p=0.024 n=9+9)
ExternalLinkCompiler 2.66s ± 3% 2.63s ± 4% ~ (p=0.280 n=10+10)
LinkWithoutDebugCompiler 338ms ± 3% 330ms ± 3% -2.21% (p=0.009 n=10+10)
StdCmd 21.5s ± 1% 20.8s ± 1% -3.27% (p=0.000 n=9+9)
[Geo mean] 615ms 597ms -2.91%
name old user-time/op new user-time/op delta
Template 344ms ± 2% 324ms ± 3% -6.01% (p=0.000 n=9+9)
Unicode 215ms ±11% 192ms ± 2% -10.84% (p=0.000 n=10+9)
GoTypes 1.99s ± 2% 1.93s ± 2% -2.73% (p=0.000 n=10+10)
Compiler 142ms ± 4% 140ms ± 3% -1.89% (p=0.031 n=9+9)
SSA 17.4s ± 1% 17.0s ± 5% ~ (p=0.113 n=9+10)
Flate 200ms ± 4% 196ms ± 6% ~ (p=0.190 n=10+10)
GoParser 388ms ± 3% 378ms ± 4% -2.59% (p=0.004 n=9+10)
Tar 278ms ± 8% 277ms ± 2% ~ (p=0.315 n=10+10)
XML 387ms ± 2% 381ms ± 2% -1.63% (p=0.005 n=8+8)
LinkCompiler 784ms ± 4% 778ms ± 2% ~ (p=0.436 n=10+10)
ExternalLinkCompiler 2.45s ± 1% 2.42s ± 1% -1.11% (p=0.001 n=10+9)
LinkWithoutDebugCompiler 374ms ± 3% 366ms ± 2% -2.15% (p=0.010 n=10+9)
[Geo mean] 600ms 583ms -2.91%
Change-Id: I9552a70d6a2ad500e9acd8815762b761be3c2ff9
Reviewed-on: https://go-review.googlesource.com/c/go/+/432897
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/compile/internal/types2"
return ir.NewAssignOpStmt(pos, op, x, bl)
}
-func idealType(tv types2.TypeAndValue) types2.Type {
+func idealType(tv syntax.TypeAndValue) types2.Type {
// The gc backend expects all expressions to have a concrete type, and
// types2 mostly satisfies this expectation already. But there are a few
// cases where the Go spec doesn't require converting to concrete type,
Sizes: &gcSizes{},
}
info := &types2.Info{
- Types: make(map[syntax.Expr]types2.TypeAndValue),
- Defs: make(map[*syntax.Name]types2.Object),
- Uses: make(map[*syntax.Name]types2.Object),
- Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
- Implicits: make(map[syntax.Node]types2.Object),
- Scopes: make(map[syntax.Node]*types2.Scope),
- Instances: make(map[*syntax.Name]types2.Instance),
+ StoreTypesInSyntax: true,
+ Defs: make(map[*syntax.Name]types2.Object),
+ Uses: make(map[*syntax.Name]types2.Object),
+ Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
+ Implicits: make(map[syntax.Node]types2.Object),
+ Scopes: make(map[syntax.Node]*types2.Scope),
+ Instances: make(map[*syntax.Name]types2.Instance),
// expand as needed
}
return g.topFuncIsGeneric
}
-func (g *irgen) typeAndValue(x syntax.Expr) types2.TypeAndValue {
- tv, ok := g.info.Types[x]
- if !ok {
+func (g *irgen) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
+ tv := x.GetTypeInfo()
+ if tv.Type == nil {
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
}
return tv
}
-func (g *irgen) type2(x syntax.Expr) types2.Type {
- tv, ok := g.info.Types[x]
- if !ok {
+func (g *irgen) type2(x syntax.Expr) syntax.Type {
+ tv := x.GetTypeInfo()
+ if tv.Type == nil {
base.FatalfAt(g.pos(x), "missing type for %v (%T)", x, x)
}
return tv.Type
pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
}
-func (pw *pkgWriter) typeAndValue(x syntax.Expr) types2.TypeAndValue {
- tv, ok := pw.info.Types[x]
- if !ok {
+func (pw *pkgWriter) typeAndValue(x syntax.Expr) syntax.TypeAndValue {
+ tv := x.GetTypeInfo()
+ if tv.Type == nil {
pw.fatalf(x, "missing Types entry: %v", syntax.String(x))
}
return tv
}
-func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (types2.TypeAndValue, bool) {
- tv, ok := pw.info.Types[x]
- return tv, ok
+func (pw *pkgWriter) maybeTypeAndValue(x syntax.Expr) (syntax.TypeAndValue, bool) {
+ tv := x.GetTypeInfo()
+ return tv, tv.Type != nil
}
// typeOf returns the Type of the given value expression.
type (
Expr interface {
Node
+ typeInfo
aExpr()
}
}
)
-type expr struct{ node }
+type expr struct {
+ node
+ typeAndValue // After typechecking, contains the results of typechecking this expression.
+}
func (*expr) aExpr() {}
--- /dev/null
+// Copyright 2022 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 syntax
+
+import "go/constant"
+
+// A Type represents a type of Go.
+// All types implement the Type interface.
+// (This type originally lived in types2. We moved it here
+// so we could depend on it from other packages without
+// introducing a circularity.)
+type Type interface {
+ // Underlying returns the underlying type of a type.
+ Underlying() Type
+
+ // String returns a string representation of a type.
+ String() string
+}
+
+// Expressions in the syntax package provide storage for
+// the typechecker to record its results. This interface
+// is the mechanism the typechecker uses to record results,
+// and clients use to retrieve those results.
+type typeInfo interface {
+ SetTypeInfo(TypeAndValue)
+ GetTypeInfo() TypeAndValue
+}
+
+// A TypeAndValue records the type information, constant
+// value if known, and various other flags associated with
+// an expression.
+// This type is similar to types2.TypeAndValue, but exposes
+// none of types2's internals.
+type TypeAndValue struct {
+ Type Type
+ Value constant.Value
+ exprFlags
+}
+
+type exprFlags uint8
+
+func (f exprFlags) IsVoid() bool { return f&1 != 0 }
+func (f exprFlags) IsType() bool { return f&2 != 0 }
+func (f exprFlags) IsBuiltin() bool { return f&4 != 0 }
+func (f exprFlags) IsValue() bool { return f&8 != 0 }
+func (f exprFlags) IsNil() bool { return f&16 != 0 }
+func (f exprFlags) Addressable() bool { return f&32 != 0 }
+func (f exprFlags) Assignable() bool { return f&64 != 0 }
+func (f exprFlags) HasOk() bool { return f&128 != 0 }
+
+func (f *exprFlags) SetIsVoid() { *f |= 1 }
+func (f *exprFlags) SetIsType() { *f |= 2 }
+func (f *exprFlags) SetIsBuiltin() { *f |= 4 }
+func (f *exprFlags) SetIsValue() { *f |= 8 }
+func (f *exprFlags) SetIsNil() { *f |= 16 }
+func (f *exprFlags) SetAddressable() { *f |= 32 }
+func (f *exprFlags) SetAssignable() { *f |= 64 }
+func (f *exprFlags) SetHasOk() { *f |= 128 }
+
+// a typeAndValue contains the results of typechecking an expression.
+// It is embedded in expression nodes.
+type typeAndValue struct {
+ tv TypeAndValue
+}
+
+func (x *typeAndValue) SetTypeInfo(tv TypeAndValue) {
+ x.tv = tv
+}
+func (x *typeAndValue) GetTypeInfo() TypeAndValue {
+ return x.tv
+}
// qualified identifiers are collected in the Uses map.
Types map[syntax.Expr]TypeAndValue
+ // If StoreTypesInSyntax is set, type information identical to
+ // that which would be put in the Types map, will be set in
+ // syntax.Expr.TypeAndValue (independently of whether Types
+ // is nil or not).
+ StoreTypesInSyntax bool
+
// Instances maps identifiers denoting generic types or functions to their
// type arguments and instantiated type.
//
InitOrder []*Initializer
}
+func (info *Info) recordTypes() bool {
+ return info.Types != nil || info.StoreTypesInSyntax
+}
+
// TypeOf returns the type of expression e, or nil if not found.
-// Precondition: the Types, Uses and Defs maps are populated.
+// Precondition 1: the Types map is populated or StoreTypesInSynax is set.
+// Precondition 2: Uses and Defs maps are populated.
func (info *Info) TypeOf(e syntax.Expr) Type {
- if t, ok := info.Types[e]; ok {
- return t.Type
+ if info.Types != nil {
+ if t, ok := info.Types[e]; ok {
+ return t.Type
+ }
+ } else if info.StoreTypesInSyntax {
+ if tv := e.GetTypeInfo(); tv.Type != nil {
+ return tv.Type
+ }
}
+
if id, _ := e.(*syntax.Name); id != nil {
if obj := info.ObjectOf(id); obj != nil {
return obj.Type()
return
}
if t := coreString(x.typ); t != nil && isString(t) {
- if check.Types != nil {
+ if check.recordTypes() {
sig := makeSig(S, S, x.typ)
sig.variadic = true
check.recordBuiltinType(call.Fun, sig)
x.mode = value
x.typ = S
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, sig)
}
}
// record the signature before changing x.typ
- if check.Types != nil && mode != constant_ {
+ if check.recordTypes() && mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ))
}
return
}
x.mode = novalue
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
}
x.mode = value
}
- if check.Types != nil && x.mode != constant_ {
+ if check.recordTypes() && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
}
return
}
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
}
x.mode = value
}
x.mode = novalue
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(nil, map_, key))
}
x.mode = value
}
- if check.Types != nil && x.mode != constant_ {
+ if check.recordTypes() && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
}
}
x.mode = value
x.typ = T
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
}
x.mode = value
x.typ = &Pointer{base: T}
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
}
}
x.mode = novalue
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
}
}
x.mode = novalue
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
}
// recover() interface{}
x.mode = value
x.typ = &emptyInterface
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ))
}
x.mode = value
x.typ = Typ[UnsafePointer]
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, x.typ, y.typ))
}
if hasVarSize(x.typ, nil) {
x.mode = value
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
}
} else {
// arranging struct fields if it wanted to.
if hasVarSize(base, nil) {
x.mode = value
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
}
} else {
if hasVarSize(x.typ, nil) {
x.mode = value
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
}
} else {
x.mode = value
x.typ = NewSlice(ptr.base)
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, ptr, y.typ))
}
x.mode = value
x.typ = NewPointer(slice.elem)
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, slice))
}
x.mode = value
x.typ = Typ[String]
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, NewPointer(universeByte), y.typ))
}
x.mode = value
x.typ = NewPointer(universeByte)
- if check.Types != nil {
+ if check.recordTypes() {
check.recordBuiltinType(call.Fun, makeSig(x.typ, Typ[String]))
}
}
func (check *Checker) recordUntyped() {
- if !debug && check.Types == nil {
+ if !debug && !check.recordTypes() {
return // nothing to do
}
if m := check.Types; m != nil {
m[x] = TypeAndValue{mode, typ, val}
}
+ if check.StoreTypesInSyntax {
+ tv := TypeAndValue{mode, typ, val}
+ stv := syntax.TypeAndValue{Type: typ, Value: val}
+ if tv.IsVoid() {
+ stv.SetIsVoid()
+ }
+ if tv.IsType() {
+ stv.SetIsType()
+ }
+ if tv.IsBuiltin() {
+ stv.SetIsBuiltin()
+ }
+ if tv.IsValue() {
+ stv.SetIsValue()
+ }
+ if tv.IsNil() {
+ stv.SetIsNil()
+ }
+ if tv.Addressable() {
+ stv.SetAddressable()
+ }
+ if tv.Assignable() {
+ stv.SetAssignable()
+ }
+ if tv.HasOk() {
+ stv.SetHasOk()
+ }
+ x.SetTypeInfo(stv)
+ }
}
func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
NewVar(pos, check.pkg, "", a[1]),
)
m[x] = tv
- // if x is a parenthesized expression (p.X), update p.X
+ p, _ := x.(*syntax.ParenExpr)
+ if p == nil {
+ break
+ }
+ x = p.X
+ }
+ }
+ if check.StoreTypesInSyntax {
+ // Note: this loop is duplicated because the type of tv is different.
+ // Above it is types2.TypeAndValue, here it is syntax.TypeAndValue.
+ for {
+ tv := x.GetTypeInfo()
+ assert(tv.Type != nil) // should have been recorded already
+ pos := x.Pos()
+ tv.Type = NewTuple(
+ NewVar(pos, check.pkg, "", a[0]),
+ NewVar(pos, check.pkg, "", a[1]),
+ )
+ x.SetTypeInfo(tv)
p, _ := x.(*syntax.ParenExpr)
if p == nil {
break
package types2
+import "cmd/compile/internal/syntax"
+
// A Type represents a type of Go.
// All types implement the Type interface.
-type Type interface {
- // Underlying returns the underlying type of a type.
- Underlying() Type
-
- // String returns a string representation of a type.
- String() string
-}
+type Type = syntax.Type
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].