1
0
mirror of https://github.com/golang/go synced 2024-11-26 08:17:59 -07:00

cmd/compile/internal/types: simplify and optimize PtrDataSize

The current implementation of PtrDataSize checks HasPointers each
call, which could lead to exponential blow-up in handling (admittedly
contrived) deeply nested structs.

To avoid the duplicate recursion, this CL incorporates the HasPointers
logic directly int PtrDataSize, and then re-defines HasPointers as
simply "PtrDataSize(t) > 0".

This CL also tightens up HasPointers/PtrDataSize to only be valid on
actual Go types. Fortunately, there was only one instance where this
wasn't already the case (escape analysis), and that's easily fixed
with an extra check for untyped types.

Change-Id: I0044bf9b558a88333aee2ccb137afb6cb4fea1db
Reviewed-on: https://go-review.googlesource.com/c/go/+/345809
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Matthew Dempsky 2021-08-27 17:14:00 -07:00
parent a9377183d0
commit c81fa001a7
3 changed files with 33 additions and 55 deletions

View File

@ -30,7 +30,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
base.Pos = lno base.Pos = lno
}() }()
if k.derefs >= 0 && !n.Type().HasPointers() { if k.derefs >= 0 && !n.Type().IsUntyped() && !n.Type().HasPointers() {
k.dst = &e.blankLoc k.dst = &e.blankLoc
} }

View File

@ -630,17 +630,23 @@ func ResumeCheckSize() {
// PtrDataSize returns the length in bytes of the prefix of t // PtrDataSize returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data. // containing pointer data. Anything after this offset is scalar data.
//
// PtrDataSize is only defined for actual Go types. It's an error to
// use it on compiler-internal types (e.g., TSSA, TRESULTS).
func PtrDataSize(t *Type) int64 { func PtrDataSize(t *Type) int64 {
if !t.HasPointers() {
return 0
}
switch t.Kind() { switch t.Kind() {
case TPTR, case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32,
TUNSAFEPTR, TUINT32, TINT64, TUINT64, TINT, TUINT,
TFUNC, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64:
TCHAN, return 0
TMAP:
case TPTR:
if t.Elem().NotInHeap() {
return 0
}
return int64(PtrSize)
case TUNSAFEPTR, TFUNC, TCHAN, TMAP:
return int64(PtrSize) return int64(PtrSize)
case TSTRING: case TSTRING:
@ -654,24 +660,32 @@ func PtrDataSize(t *Type) int64 {
return 2 * int64(PtrSize) return 2 * int64(PtrSize)
case TSLICE: case TSLICE:
if t.Elem().NotInHeap() {
return 0
}
// struct { byte *array; uintgo len; uintgo cap; } // struct { byte *array; uintgo len; uintgo cap; }
return int64(PtrSize) return int64(PtrSize)
case TARRAY: case TARRAY:
// haspointers already eliminated t.NumElem() == 0. if t.NumElem() == 0 {
return (t.NumElem()-1)*t.Elem().width + PtrDataSize(t.Elem()) return 0
}
// t.NumElem() > 0
size := PtrDataSize(t.Elem())
if size == 0 {
return 0
}
return (t.NumElem()-1)*t.Elem().Size() + size
case TSTRUCT: case TSTRUCT:
// Find the last field that has pointers. // Find the last field that has pointers, if any.
var lastPtrField *Field
fs := t.Fields().Slice() fs := t.Fields().Slice()
for i := len(fs) - 1; i >= 0; i-- { for i := len(fs) - 1; i >= 0; i-- {
if fs[i].Type.HasPointers() { if size := PtrDataSize(fs[i].Type); size > 0 {
lastPtrField = fs[i] return fs[i].Offset + size
break
} }
} }
return lastPtrField.Offset + PtrDataSize(lastPtrField.Type) return 0
default: default:
base.Fatalf("PtrDataSize: unexpected type, %v", t) base.Fatalf("PtrDataSize: unexpected type, %v", t)

View File

@ -1681,43 +1681,7 @@ func (t *Type) IsUntyped() bool {
// HasPointers reports whether t contains a heap pointer. // HasPointers reports whether t contains a heap pointer.
// Note that this function ignores pointers to go:notinheap types. // Note that this function ignores pointers to go:notinheap types.
func (t *Type) HasPointers() bool { func (t *Type) HasPointers() bool {
switch t.kind { return PtrDataSize(t) > 0
case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
return false
case TARRAY:
if t.NumElem() == 0 { // empty array has no pointers
return false
}
return t.Elem().HasPointers()
case TSTRUCT:
for _, t1 := range t.Fields().Slice() {
if t1.Type.HasPointers() {
return true
}
}
return false
case TPTR, TSLICE:
return !t.Elem().NotInHeap()
case TTUPLE:
ttup := t.extra.(*Tuple)
return ttup.first.HasPointers() || ttup.second.HasPointers()
case TRESULTS:
types := t.extra.(*Results).Types
for _, et := range types {
if et.HasPointers() {
return true
}
}
return false
}
return true
} }
// Tie returns 'T' if t is a concrete type, // Tie returns 'T' if t is a concrete type,