mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
284 lines
7.7 KiB
Go
284 lines
7.7 KiB
Go
// 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 noder
|
|
|
|
import (
|
|
"go/constant"
|
|
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/syntax"
|
|
"cmd/compile/internal/typecheck"
|
|
"cmd/compile/internal/types"
|
|
"cmd/compile/internal/types2"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
// Helpers for constructing typed IR nodes.
|
|
//
|
|
// TODO(mdempsky): Move into their own package so they can be easily
|
|
// reused by iimport and frontend optimizations.
|
|
|
|
type ImplicitNode interface {
|
|
ir.Node
|
|
SetImplicit(x bool)
|
|
}
|
|
|
|
// Implicit returns n after marking it as Implicit.
|
|
func Implicit(n ImplicitNode) ImplicitNode {
|
|
n.SetImplicit(true)
|
|
return n
|
|
}
|
|
|
|
// typed returns n after setting its type to typ.
|
|
func typed(typ *types.Type, n ir.Node) ir.Node {
|
|
n.SetType(typ)
|
|
n.SetTypecheck(1)
|
|
return n
|
|
}
|
|
|
|
// Values
|
|
|
|
func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node {
|
|
orig := ir.NewRawOrigExpr(pos, op, raw)
|
|
return ir.NewConstExpr(val, typed(typ, orig))
|
|
}
|
|
|
|
// FixValue returns val after converting and truncating it as
|
|
// appropriate for typ.
|
|
func FixValue(typ *types.Type, val constant.Value) constant.Value {
|
|
assert(typ.Kind() != types.TFORW)
|
|
switch {
|
|
case typ.IsInteger():
|
|
val = constant.ToInt(val)
|
|
case typ.IsFloat():
|
|
val = constant.ToFloat(val)
|
|
case typ.IsComplex():
|
|
val = constant.ToComplex(val)
|
|
}
|
|
if !typ.IsUntyped() {
|
|
val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val()
|
|
}
|
|
if !typ.IsTypeParam() {
|
|
ir.AssertValidTypeForConst(typ, val)
|
|
}
|
|
return val
|
|
}
|
|
|
|
func Nil(pos src.XPos, typ *types.Type) ir.Node {
|
|
return typed(typ, ir.NewNilExpr(pos))
|
|
}
|
|
|
|
// Expressions
|
|
|
|
func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
|
|
n := typecheck.NodAddrAt(pos, x)
|
|
typed(types.NewPtr(x.Type()), n)
|
|
return n
|
|
}
|
|
|
|
func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
|
|
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
|
|
}
|
|
|
|
func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExpr {
|
|
switch op {
|
|
case ir.OADD:
|
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
|
typed(typ, n)
|
|
return n
|
|
default:
|
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
|
typed(x.Type(), n)
|
|
return n
|
|
}
|
|
}
|
|
|
|
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr {
|
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
func Deref(pos src.XPos, typ *types.Type, x ir.Node) *ir.StarExpr {
|
|
n := ir.NewStarExpr(pos, x)
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
|
op, typ := ir.ODOT, x.Type()
|
|
if typ.IsPtr() {
|
|
op, typ = ir.ODOTPTR, typ.Elem()
|
|
}
|
|
if !typ.IsStruct() {
|
|
base.FatalfAt(pos, "DotField of non-struct: %L", x)
|
|
}
|
|
|
|
// TODO(mdempsky): This is the backend's responsibility.
|
|
types.CalcSize(typ)
|
|
|
|
field := typ.Field(index)
|
|
return dot(pos, field.Type, op, x, field)
|
|
}
|
|
|
|
func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
|
method := method(x.Type(), index)
|
|
|
|
// Method value.
|
|
typ := typecheck.NewMethodType(method.Type, nil)
|
|
return dot(pos, typ, ir.OMETHVALUE, x, method)
|
|
}
|
|
|
|
// MethodExpr returns a OMETHEXPR node with the indicated index into the methods
|
|
// of typ. The receiver type is set from recv, which is different from typ if the
|
|
// method was accessed via embedded fields. Similarly, the X value of the
|
|
// ir.SelectorExpr is recv, the original OTYPE node before passing through the
|
|
// embedded fields.
|
|
func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr {
|
|
method := method(embed, index)
|
|
typ := typecheck.NewMethodType(method.Type, recv.Type())
|
|
// The method expression T.m requires a wrapper when T
|
|
// is different from m's declared receiver type. We
|
|
// normally generate these wrappers while writing out
|
|
// runtime type descriptors, which is always done for
|
|
// types declared at package scope. However, we need
|
|
// to make sure to generate wrappers for anonymous
|
|
// receiver types too.
|
|
if recv.Sym() == nil {
|
|
typecheck.NeedRuntimeType(recv.Type())
|
|
}
|
|
return dot(pos, typ, ir.OMETHEXPR, recv, method)
|
|
}
|
|
|
|
func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
|
|
n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
|
|
n.Selection = selection
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
// TODO(mdempsky): Move to package types.
|
|
func method(typ *types.Type, index int) *types.Field {
|
|
if typ.IsInterface() {
|
|
return typ.AllMethods().Index(index)
|
|
}
|
|
return types.ReceiverBaseType(typ).Methods().Index(index)
|
|
}
|
|
|
|
func Index(pos src.XPos, typ *types.Type, x, index ir.Node) *ir.IndexExpr {
|
|
n := ir.NewIndexExpr(pos, x, index)
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) *ir.SliceExpr {
|
|
op := ir.OSLICE
|
|
if max != nil {
|
|
op = ir.OSLICE3
|
|
}
|
|
n := ir.NewSliceExpr(pos, op, x, low, high, max)
|
|
typed(typ, n)
|
|
return n
|
|
}
|
|
|
|
func Unary(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node) ir.Node {
|
|
switch op {
|
|
case ir.OADDR:
|
|
return Addr(pos, x)
|
|
case ir.ODEREF:
|
|
return Deref(pos, typ, x)
|
|
}
|
|
|
|
if op == ir.ORECV {
|
|
if typ.IsFuncArgStruct() && typ.NumFields() == 2 {
|
|
// Remove the second boolean type (if provided by type2),
|
|
// since that works better with the rest of the compiler
|
|
// (which will add it back in later).
|
|
assert(typ.Field(1).Type.Kind() == types.TBOOL)
|
|
typ = typ.Field(0).Type
|
|
}
|
|
}
|
|
return typed(typ, ir.NewUnaryExpr(pos, op, x))
|
|
}
|
|
|
|
// Statements
|
|
|
|
var one = constant.MakeInt64(1)
|
|
|
|
func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
|
|
assert(x.Type() != nil)
|
|
bl := ir.NewBasicLit(pos, one)
|
|
if x.Type().HasTParam() {
|
|
// If the operand is generic, then types2 will have proved it must be
|
|
// a type that fits with increment/decrement, so just set the type of
|
|
// "one" to n.Type(). This works even for types that are eventually
|
|
// float or complex.
|
|
typed(x.Type(), bl)
|
|
} else {
|
|
bl = typecheck.DefaultLit(bl, x.Type())
|
|
}
|
|
return ir.NewAssignOpStmt(pos, op, x, bl)
|
|
}
|
|
|
|
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,
|
|
// and so types2 leaves them untyped. So we need to fix those up here.
|
|
typ := tv.Type
|
|
if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
|
|
switch basic.Kind() {
|
|
case types2.UntypedNil:
|
|
// ok; can appear in type switch case clauses
|
|
// TODO(mdempsky): Handle as part of type switches instead?
|
|
case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
|
|
// Untyped rhs of non-constant shift, e.g. x << 1.0.
|
|
// If we have a constant value, it must be an int >= 0.
|
|
if tv.Value != nil {
|
|
s := constant.ToInt(tv.Value)
|
|
assert(s.Kind() == constant.Int && constant.Sign(s) >= 0)
|
|
}
|
|
typ = types2.Typ[types2.Uint]
|
|
case types2.UntypedBool:
|
|
typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
|
|
case types2.UntypedString:
|
|
typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
return typ
|
|
}
|
|
|
|
func isTypeParam(t types2.Type) bool {
|
|
_, ok := t.(*types2.TypeParam)
|
|
return ok
|
|
}
|
|
|
|
// isNotInHeap reports whether typ is or contains an element of type
|
|
// runtime/internal/sys.NotInHeap.
|
|
func isNotInHeap(typ types2.Type) bool {
|
|
if named, ok := typ.(*types2.Named); ok {
|
|
if obj := named.Obj(); obj.Name() == "nih" && obj.Pkg().Path() == "runtime/internal/sys" {
|
|
return true
|
|
}
|
|
typ = named.Underlying()
|
|
}
|
|
|
|
switch typ := typ.(type) {
|
|
case *types2.Array:
|
|
return isNotInHeap(typ.Elem())
|
|
case *types2.Struct:
|
|
for i := 0; i < typ.NumFields(); i++ {
|
|
if isNotInHeap(typ.Field(i).Type()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
default:
|
|
return false
|
|
}
|
|
}
|