]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: keep typecheck results in syntax tree
authorKeith Randall <khr@golang.org>
Fri, 2 Sep 2022 21:11:09 +0000 (14:11 -0700)
committerKeith Randall <khr@google.com>
Thu, 29 Sep 2022 20:55:01 +0000 (20:55 +0000)
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>
src/cmd/compile/internal/noder/helpers.go
src/cmd/compile/internal/noder/irgen.go
src/cmd/compile/internal/noder/writer.go
src/cmd/compile/internal/syntax/nodes.go
src/cmd/compile/internal/syntax/type.go [new file with mode: 0644]
src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/type.go

index 764dcb3f85bb76a4e93898d8e275c39900bdf81b..4ef46a477b09f3fc27f24cc6e7ebe4884a717844 100644 (file)
@@ -9,6 +9,7 @@ import (
 
        "cmd/compile/internal/base"
        "cmd/compile/internal/ir"
+       "cmd/compile/internal/syntax"
        "cmd/compile/internal/typecheck"
        "cmd/compile/internal/types"
        "cmd/compile/internal/types2"
@@ -222,7 +223,7 @@ func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
        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,
index 4f399066e352b232ce836806f40e833eba029183..57872bce276e4dd0d967fe632a5ef9ad67e56841 100644 (file)
@@ -59,13 +59,13 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
                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
        }
 
@@ -390,17 +390,17 @@ func (g *irgen) delayTransform() bool {
        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
index b39a57a13f4ae82f7b0d9563d71d8c5946edf58d..9f432935271a8c772cee70091ed30142c7053cbd 100644 (file)
@@ -120,16 +120,16 @@ func (pw *pkgWriter) unexpected(what string, p poser) {
        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.
index 10af3c597b03e24945b3568cb787201e9b6a098f..e943a9a9e672c97e7dc947bceb3b5925aa99e395 100644 (file)
@@ -132,6 +132,7 @@ func NewName(pos Pos, value string) *Name {
 type (
        Expr interface {
                Node
+               typeInfo
                aExpr()
        }
 
@@ -308,7 +309,10 @@ type (
        }
 )
 
-type expr struct{ node }
+type expr struct {
+       node
+       typeAndValue // After typechecking, contains the results of typechecking this expression.
+}
 
 func (*expr) aExpr() {}
 
diff --git a/src/cmd/compile/internal/syntax/type.go b/src/cmd/compile/internal/syntax/type.go
new file mode 100644 (file)
index 0000000..01eab7a
--- /dev/null
@@ -0,0 +1,73 @@
+// 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
+}
index cbd49b68c757952c3e09a82ec2fa716dfa9bb10f..1f19fe0927c6ff6eab46dfa07b31dea35e6d5735 100644 (file)
@@ -197,6 +197,12 @@ type Info struct {
        // 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.
        //
@@ -276,12 +282,24 @@ type Info struct {
        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()
index 6266fbd67d67ed6718e0c70fb91812fdaede431d..9e0a51053257f492bec93b0976cb712f34d6f34f 100644 (file)
@@ -115,7 +115,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                                        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)
@@ -147,7 +147,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                x.mode = value
                x.typ = S
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, sig)
                }
 
@@ -223,7 +223,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
 
                // 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))
                }
 
@@ -248,7 +248,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                        return
                }
                x.mode = novalue
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
                }
 
@@ -340,7 +340,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                        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))
                }
 
@@ -371,7 +371,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                        return
                }
 
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
                }
                x.mode = value
@@ -410,7 +410,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
 
                x.mode = novalue
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(nil, map_, key))
                }
 
@@ -476,7 +476,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                        x.mode = value
                }
 
-               if check.Types != nil && x.mode != constant_ {
+               if check.recordTypes() && x.mode != constant_ {
                        check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
                }
 
@@ -525,7 +525,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
                x.mode = value
                x.typ = T
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
                }
 
@@ -539,7 +539,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                x.mode = value
                x.typ = &Pointer{base: T}
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
                }
 
@@ -564,7 +564,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
 
                x.mode = novalue
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
                }
 
@@ -588,7 +588,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
 
                x.mode = novalue
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(nil, params...))
                }
 
@@ -596,7 +596,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                // recover() interface{}
                x.mode = value
                x.typ = &emptyInterface
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ))
                }
 
@@ -620,7 +620,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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))
                }
 
@@ -633,7 +633,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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 {
@@ -697,7 +697,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                // 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 {
@@ -716,7 +716,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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 {
@@ -747,7 +747,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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))
                }
 
@@ -766,7 +766,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                x.mode = value
                x.typ = NewPointer(slice.elem)
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, slice))
                }
 
@@ -790,7 +790,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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))
                }
 
@@ -808,7 +808,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                x.mode = value
                x.typ = NewPointer(universeByte)
-               if check.Types != nil {
+               if check.recordTypes() {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ, Typ[String]))
                }
 
index 7b8a6e78c8fc4b17c517d3e79bb237a0c7cb4ddc..2dc960dfc82a368b4009a2fba1397098da17b257 100644 (file)
@@ -425,7 +425,7 @@ func (check *Checker) record(x *operand) {
 }
 
 func (check *Checker) recordUntyped() {
-       if !debug && check.Types == nil {
+       if !debug && !check.recordTypes() {
                return // nothing to do
        }
 
@@ -453,6 +453,35 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty
        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) {
@@ -489,7 +518,25 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
                                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
index 0fe39dbca4645d9102999ff2970850d16737efc0..92ecf11559e61cca472d07b52b07323e2c82058f 100644 (file)
@@ -4,15 +4,11 @@
 
 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].