From 4b9a70a3b7101d01c67f1c7f839ab4f1234fda2e Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sun, 20 Aug 2023 14:33:32 -0700 Subject: [PATCH] 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 Reviewed-by: Than McIntosh Auto-Submit: Matthew Dempsky TryBot-Bypass: Matthew Dempsky Reviewed-by: Cuong Manh Le --- src/cmd/compile/internal/types/sizeof_test.go | 2 +- src/cmd/compile/internal/types/type.go | 98 +++++++++++-------- 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go index cec1283435..8a6f24124a 100644 --- a/src/cmd/compile/internal/types/sizeof_test.go +++ b/src/cmd/compile/internal/types/sizeof_test.go @@ -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}, diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index f03aabe430..d80a03fa08 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -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) } -- 2.48.1