Skip to content

Commit fcfd824

Browse files
committed
cmd/compile: compute type eq/hash algorithm in CalcSize instead of on demand
For #65540 Actually more correct in some very weird, and probably impossible to trigger currently, cases. For instance, a struct with a NOEQ and a NOALG field (the old code would not report the noalg bit). Change-Id: I36c473b59aa5775d8a520ac746b114d16a22699d Reviewed-on: https://go-review.googlesource.com/c/go/+/571542 Reviewed-by: Matthew Dempsky <[email protected]> Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent d255792 commit fcfd824

File tree

4 files changed

+111
-130
lines changed

4 files changed

+111
-130
lines changed

src/cmd/compile/internal/types/alg.go

Lines changed: 25 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import "cmd/compile/internal/base"
88

99
// AlgKind describes the kind of algorithms used for comparing and
1010
// hashing a Type.
11-
type AlgKind int
11+
type AlgKind int8
1212

1313
//go:generate stringer -type AlgKind -trimprefix A alg.go
1414

1515
const (
16-
ANOEQ AlgKind = iota
17-
AMEM0
16+
AUNK AlgKind = iota
17+
ANOEQ // Types cannot be compared
18+
ANOALG // implies ANOEQ, and in addition has a part that is marked Noalg
19+
AMEM // Type can be compared/hashed as regular memory.
20+
AMEM0 // Specific subvariants of AMEM (TODO: move to ../reflectdata?)
1821
AMEM8
1922
AMEM16
2023
AMEM32
@@ -27,105 +30,30 @@ const (
2730
AFLOAT64
2831
ACPLX64
2932
ACPLX128
30-
ANOALG // implies ANOEQ, and in addition has a part that is marked Noalg
31-
32-
// Type can be compared/hashed as regular memory.
33-
AMEM AlgKind = 100
34-
35-
// Type needs special comparison/hashing functions.
36-
ASPECIAL AlgKind = -1
33+
ASPECIAL // Type needs special comparison/hashing functions.
3734
)
3835

39-
// AlgType returns the AlgKind used for comparing and hashing Type t.
40-
func AlgType(t *Type) AlgKind {
41-
if t.Noalg() {
42-
return ANOALG
43-
}
44-
45-
switch t.Kind() {
46-
case TANY, TFORW:
47-
// will be defined later.
48-
return ANOEQ
49-
50-
case TINT8, TUINT8, TINT16, TUINT16,
51-
TINT32, TUINT32, TINT64, TUINT64,
52-
TINT, TUINT, TUINTPTR,
53-
TBOOL, TPTR,
54-
TCHAN, TUNSAFEPTR:
55-
return AMEM
56-
57-
case TFUNC, TMAP:
58-
return ANOEQ
59-
60-
case TFLOAT32:
61-
return AFLOAT32
62-
63-
case TFLOAT64:
64-
return AFLOAT64
65-
66-
case TCOMPLEX64:
67-
return ACPLX64
68-
69-
case TCOMPLEX128:
70-
return ACPLX128
71-
72-
case TSTRING:
73-
return ASTRING
74-
75-
case TINTER:
76-
if t.IsEmptyInterface() {
77-
return ANILINTER
78-
}
79-
return AINTER
80-
81-
case TSLICE:
82-
return ANOEQ
36+
// Most kinds are priority 0. Higher numbers are higher priority, in that
37+
// the higher priority kinds override lower priority kinds.
38+
var algPriority = [ASPECIAL + 1]int8{ASPECIAL: 1, ANOEQ: 2, ANOALG: 3, AMEM: -1}
8339

84-
case TARRAY:
85-
a := AlgType(t.Elem())
86-
if a == AMEM || a == ANOEQ || a == ANOALG {
87-
return a
88-
}
89-
90-
switch t.NumElem() {
91-
case 0:
92-
// We checked above that the element type is comparable.
93-
return AMEM
94-
case 1:
95-
// Single-element array is same as its lone element.
96-
return a
97-
}
98-
99-
return ASPECIAL
100-
101-
case TSTRUCT:
102-
fields := t.Fields()
103-
104-
// One-field struct is same as that one field alone.
105-
if len(fields) == 1 && !fields[0].Sym.IsBlank() {
106-
return AlgType(fields[0].Type)
107-
}
108-
109-
ret := AMEM
110-
for i, f := range fields {
111-
// All fields must be comparable.
112-
a := AlgType(f.Type)
113-
if a == ANOEQ || a == ANOALG {
114-
return a
115-
}
116-
117-
// Blank fields, padded fields, fields with non-memory
118-
// equality need special compare.
119-
if a != AMEM || f.Sym.IsBlank() || IsPaddedField(t, i) {
120-
ret = ASPECIAL
121-
}
122-
}
123-
124-
return ret
40+
// setAlg sets the algorithm type of t to a, if it is of higher
41+
// priority to the current algorithm type.
42+
func (t *Type) setAlg(a AlgKind) {
43+
if t.alg == AUNK {
44+
base.Fatalf("setAlg(%v,%s) starting with unknown priority", t, a)
45+
}
46+
if algPriority[a] > algPriority[t.alg] {
47+
t.alg = a
48+
} else if a != t.alg && algPriority[a] == algPriority[t.alg] {
49+
base.Fatalf("ambiguous priority %s and %s", a, t.alg)
12550
}
51+
}
12652

127-
base.Fatalf("AlgType: unexpected type %v", t)
128-
return 0
53+
// AlgType returns the AlgKind used for comparing and hashing Type t.
54+
func AlgType(t *Type) AlgKind {
55+
CalcSize(t)
56+
return t.alg
12957
}
13058

13159
// TypeHasNoAlg reports whether t does not have any associated hash/eq

src/cmd/compile/internal/types/algkind_string.go

Lines changed: 22 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/types/size.go

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func isAtomicStdPkg(p *Pkg) bool {
202202
return p.Prefix == "sync/atomic" || p.Prefix == "runtime/internal/atomic"
203203
}
204204

205-
// CalcSize calculates and stores the size and alignment for t.
205+
// CalcSize calculates and stores the size, alignment, and eq/hash algorithm for t.
206206
// If CalcSizeDisabled is set, and the size/alignment
207207
// have not already been calculated, it calls Fatal.
208208
// This is used to prevent data races in the back end.
@@ -245,7 +245,11 @@ func CalcSize(t *Type) {
245245
}
246246

247247
t.width = -2
248-
t.align = 0 // 0 means use t.Width, below
248+
t.align = 0 // 0 means use t.Width, below
249+
t.alg = AMEM // default
250+
if t.Noalg() {
251+
t.setAlg(ANOALG)
252+
}
249253

250254
et := t.Kind()
251255
switch et {
@@ -286,21 +290,25 @@ func CalcSize(t *Type) {
286290
case TFLOAT32:
287291
w = 4
288292
t.floatRegs = 1
293+
t.setAlg(AFLOAT32)
289294

290295
case TFLOAT64:
291296
w = 8
292297
t.align = uint8(RegSize)
293298
t.floatRegs = 1
299+
t.setAlg(AFLOAT64)
294300

295301
case TCOMPLEX64:
296302
w = 8
297303
t.align = 4
298304
t.floatRegs = 2
305+
t.setAlg(ACPLX64)
299306

300307
case TCOMPLEX128:
301308
w = 16
302309
t.align = uint8(RegSize)
303310
t.floatRegs = 2
311+
t.setAlg(ACPLX128)
304312

305313
case TPTR:
306314
w = int64(PtrSize)
@@ -316,6 +324,11 @@ func CalcSize(t *Type) {
316324
t.align = uint8(PtrSize)
317325
t.intRegs = 2
318326
expandiface(t)
327+
if len(t.allMethods.Slice()) == 0 {
328+
t.setAlg(ANILINTER)
329+
} else {
330+
t.setAlg(AINTER)
331+
}
319332

320333
case TCHAN: // implemented as pointer
321334
w = int64(PtrSize)
@@ -346,6 +359,7 @@ func CalcSize(t *Type) {
346359
t.intRegs = 1
347360
CheckSize(t.Elem())
348361
CheckSize(t.Key())
362+
t.setAlg(ANOEQ)
349363

350364
case TFORW: // should have been filled in
351365
base.Fatalf("invalid recursive type %v", t)
@@ -360,6 +374,7 @@ func CalcSize(t *Type) {
360374
w = StringSize
361375
t.align = uint8(PtrSize)
362376
t.intRegs = 2
377+
t.setAlg(ASTRING)
363378

364379
case TARRAY:
365380
if t.Elem() == nil {
@@ -390,6 +405,21 @@ func CalcSize(t *Type) {
390405
t.intRegs = math.MaxUint8
391406
t.floatRegs = math.MaxUint8
392407
}
408+
switch a := t.Elem().alg; a {
409+
case AMEM, ANOEQ, ANOALG:
410+
t.setAlg(a)
411+
default:
412+
switch t.NumElem() {
413+
case 0:
414+
// We checked above that the element type is comparable.
415+
t.setAlg(AMEM)
416+
case 1:
417+
// Single-element array is same as its lone element.
418+
t.setAlg(a)
419+
default:
420+
t.setAlg(ASPECIAL)
421+
}
422+
}
393423

394424
case TSLICE:
395425
if t.Elem() == nil {
@@ -399,6 +429,7 @@ func CalcSize(t *Type) {
399429
CheckSize(t.Elem())
400430
t.align = uint8(PtrSize)
401431
t.intRegs = 3
432+
t.setAlg(ANOEQ)
402433

403434
case TSTRUCT:
404435
if t.IsFuncArgStruct() {
@@ -414,6 +445,7 @@ func CalcSize(t *Type) {
414445
CheckSize(t1)
415446
w = int64(PtrSize) // width of func type is pointer
416447
t.intRegs = 1
448+
t.setAlg(ANOEQ)
417449

418450
// function is 3 cated structures;
419451
// compute their widths as side-effect.
@@ -502,6 +534,32 @@ func CalcStructSize(t *Type) {
502534
t.align = maxAlign
503535
t.intRegs = uint8(intRegs)
504536
t.floatRegs = uint8(floatRegs)
537+
538+
// Compute eq/hash algorithm type.
539+
t.alg = AMEM // default
540+
if t.Noalg() {
541+
t.setAlg(ANOALG)
542+
}
543+
if len(fields) == 1 && !fields[0].Sym.IsBlank() {
544+
// One-field struct is same as that one field alone.
545+
t.setAlg(fields[0].Type.alg)
546+
} else {
547+
for i, f := range fields {
548+
a := f.Type.alg
549+
switch a {
550+
case ANOEQ, ANOALG:
551+
case AMEM:
552+
// Blank fields and padded fields need a special compare.
553+
if f.Sym.IsBlank() || IsPaddedField(t, i) {
554+
a = ASPECIAL
555+
}
556+
default:
557+
// Fields with non-memory equality need a special compare.
558+
a = ASPECIAL
559+
}
560+
t.setAlg(a)
561+
}
562+
}
505563
}
506564

507565
func (t *Type) widthCalculated() bool {

src/cmd/compile/internal/types/type.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ type Type struct {
201201
intRegs, floatRegs uint8 // registers needed for ABIInternal
202202

203203
flags bitset8
204+
alg AlgKind // valid if Align > 0
204205

205206
// For defined (named) generic types, a pointer to the list of type params
206207
// (in order) of this type that need to be instantiated. For instantiated
@@ -657,8 +658,10 @@ func NewPtr(elem *Type) *Type {
657658
if elem.HasShape() {
658659
t.SetHasShape(true)
659660
}
661+
t.alg = AMEM
660662
if elem.Noalg() {
661663
t.SetNoalg(true)
664+
t.alg = ANOALG
662665
}
663666
return t
664667
}
@@ -1660,6 +1663,7 @@ func (t *Type) SetUnderlying(underlying *Type) {
16601663
t.extra = underlying.extra
16611664
t.width = underlying.width
16621665
t.align = underlying.align
1666+
t.alg = underlying.alg
16631667
t.intRegs = underlying.intRegs
16641668
t.floatRegs = underlying.floatRegs
16651669
t.underlying = underlying.underlying

0 commit comments

Comments
 (0)