cmd/compile/internal/types: simpler signature type representation

Now that all of the uses of signature types have been cleaned up, we
can simplify the internal representation significantly.

In particular, instead of 3 separate struct objects each with 3
separate slices of fields, we can store all of the parameters in a
single slice and track the boundaries between them.

We still need a results tuple struct for representing the type of
multi-value call expressions, but just a single one and it can safely
reuse the results subsection of the full parameters slice.

Note: while Sizeof(Func) has increased (e.g., 32->56 on amd64), we're
saving on the allocation of 2 Types, 2 Structs, and 2 []*Field (288
bytes total on amd64), not counting any extra GC size class padding
from using a single shared []*Field instead of 3 separate ones.

Change-Id: I119b5e960e715b3bc4f1f726e58b910a098659da
Reviewed-on: https://go-review.googlesource.com/c/go/+/521335
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
TryBot-Bypass: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Matthew Dempsky 2023-08-20 14:33:32 -07:00 committed by Gopher Robot
parent b0a17c0489
commit 4b9a70a3b7
2 changed files with 60 additions and 40 deletions

View File

@ -24,7 +24,7 @@ func TestSizeof(t *testing.T) {
{Type{}, 56, 96},
{Map{}, 12, 24},
{Forward{}, 20, 32},
{Func{}, 20, 32},
{Func{}, 32, 56},
{Struct{}, 12, 24},
{Interface{}, 0, 0},
{Chan{}, 8, 16},

View File

@ -299,9 +299,12 @@ func (t *Type) forwardType() *Forward {
// Func contains Type fields specific to func types.
type Func struct {
Receiver *Type // function receiver
Results *Type // function results
Params *Type // function params
allParams []*Field // slice of all parameters, in receiver/params/results order
startParams int // index of the start of the (regular) parameters section
startResults int // index of the start of the results section
resultsTuple *Type // struct-like type representing multi-value results
// Argwid is the total width of the function receiver, params, and results.
// It gets calculated via a temporary TFUNCARGS type.
@ -309,6 +312,10 @@ type Func struct {
Argwid int64
}
func (ft *Func) recvs() []*Field { return ft.allParams[:ft.startParams] }
func (ft *Func) params() []*Field { return ft.allParams[ft.startParams:ft.startResults] }
func (ft *Func) results() []*Field { return ft.allParams[ft.startResults:] }
// funcType returns t's extra func-specific fields.
func (t *Type) funcType() *Func {
t.wantEtype(TFUNC)
@ -702,27 +709,23 @@ func SubstAny(t *Type, types *[]*Type) *Type {
}
case TFUNC:
recvs := SubstAny(t.recvsTuple(), types)
params := SubstAny(t.paramsTuple(), types)
results := SubstAny(t.ResultsTuple(), types)
if recvs != t.recvsTuple() || params != t.paramsTuple() || results != t.ResultsTuple() {
t = t.copy()
t.funcType().Receiver = recvs
t.funcType().Results = results
t.funcType().Params = params
}
ft := t.funcType()
allParams := substFields(ft.allParams, types)
t = t.copy()
ft = t.funcType()
ft.allParams = allParams
rt := ft.resultsTuple
rt = rt.copy()
ft.resultsTuple = rt
rt.setFields(t.Results())
case TSTRUCT:
// Make a copy of all fields, including ones whose type does not change.
// This prevents aliasing across functions, which can lead to later
// fields getting their Offset incorrectly overwritten.
fields := t.Fields()
nfs := make([]*Field, len(fields))
for i, f := range fields {
nft := SubstAny(f.Type, types)
nfs[i] = f.Copy()
nfs[i].Type = nft
}
nfs := substFields(t.Fields(), types)
t = t.copy()
t.setFields(nfs)
}
@ -730,6 +733,16 @@ func SubstAny(t *Type, types *[]*Type) *Type {
return t
}
func substFields(fields []*Field, types *[]*Type) []*Field {
nfs := make([]*Field, len(fields))
for i, f := range fields {
nft := SubstAny(f.Type, types)
nfs[i] = f.Copy()
nfs[i].Type = nft
}
return nfs
}
// copy returns a shallow copy of the Type.
func (t *Type) copy() *Type {
if t == nil {
@ -780,26 +793,23 @@ func (t *Type) wantEtype(et Kind) {
}
}
func (t *Type) recvsTuple() *Type { return t.funcType().Receiver }
func (t *Type) paramsTuple() *Type { return t.funcType().Params }
// ResultTuple returns the result type of signature type t as a tuple.
// This can be used as the type of multi-valued call expressions.
func (t *Type) ResultsTuple() *Type { return t.funcType().Results }
func (t *Type) ResultsTuple() *Type { return t.funcType().resultsTuple }
// Recvs returns a slice of receiver parameters of signature type t.
// The returned slice always has length 0 or 1.
func (t *Type) Recvs() []*Field { return t.funcType().Receiver.Fields() }
func (t *Type) Recvs() []*Field { return t.funcType().recvs() }
// Params returns a slice of regular parameters of signature type t.
func (t *Type) Params() []*Field { return t.funcType().Params.Fields() }
func (t *Type) Params() []*Field { return t.funcType().params() }
// Results returns a slice of result parameters of signature type t.
func (t *Type) Results() []*Field { return t.funcType().Results.Fields() }
func (t *Type) Results() []*Field { return t.funcType().results() }
func (t *Type) NumRecvs() int { return t.funcType().Receiver.NumFields() }
func (t *Type) NumParams() int { return t.funcType().Params.NumFields() }
func (t *Type) NumResults() int { return t.funcType().Results.NumFields() }
func (t *Type) NumRecvs() int { return len(t.Recvs()) }
func (t *Type) NumParams() int { return len(t.Params()) }
func (t *Type) NumResults() int { return len(t.Results()) }
// IsVariadic reports whether function type t is variadic.
func (t *Type) IsVariadic() bool {
@ -809,11 +819,10 @@ func (t *Type) IsVariadic() bool {
// Recv returns the receiver of function type t, if any.
func (t *Type) Recv() *Field {
s := t.recvsTuple()
if s.NumFields() == 0 {
return nil
if s := t.Recvs(); len(s) == 1 {
return s[0]
}
return s.Field(0)
return nil
}
// Param returns the i'th parameter of signature type t.
@ -1688,10 +1697,18 @@ func NewInterface(methods []*Field) *Type {
// NewSignature returns a new function type for the given receiver,
// parameters, and results, any of which may be nil.
func NewSignature(recv *Field, params, results []*Field) *Type {
var recvs []*Field
startParams := 0
if recv != nil {
recvs = []*Field{recv}
startParams = 1
}
startResults := startParams + len(params)
allParams := make([]*Field, startResults+len(results))
if recv != nil {
allParams[0] = recv
}
copy(allParams[startParams:], params)
copy(allParams[startResults:], results)
t := newType(TFUNC)
ft := t.funcType()
@ -1702,10 +1719,13 @@ func NewSignature(recv *Field, params, results []*Field) *Type {
return s
}
ft.Receiver = funargs(recvs)
ft.Params = funargs(params)
ft.Results = funargs(results)
if fieldsHasShape(recvs) || fieldsHasShape(params) || fieldsHasShape(results) {
ft.allParams = allParams
ft.startParams = startParams
ft.startResults = startResults
ft.resultsTuple = funargs(allParams[startResults:])
if fieldsHasShape(allParams) {
t.SetHasShape(true)
}