From: Matthew Dempsky Date: Tue, 8 Mar 2016 11:40:50 +0000 (-0800) Subject: cmd/compile: simplify eq and hash function generation X-Git-Tag: go1.7beta1~1487 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=199cc194acf58008c9d4ea9a106f6302a5503f9c;p=gostls13.git cmd/compile: simplify eq and hash function generation Passes toolstash -cmp. Change-Id: Ie4675e6f713c3bbb90556f5347cbd7268a9c1a5d Reviewed-on: https://go-review.googlesource.com/20357 Run-TryBot: Matthew Dempsky TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go index 1cddab1784..5105178732 100644 --- a/src/cmd/compile/internal/gc/alg.go +++ b/src/cmd/compile/internal/gc/alg.go @@ -144,18 +144,16 @@ func algtype1(t *Type, bad **Type) int { } ret := AMEM - var a int - for t1 := t.Type; t1 != nil; t1 = t1.Down { + for f := t.Type; f != nil; f = f.Down { // All fields must be comparable. - a = algtype1(t1.Type, bad) - + a := algtype1(f.Type, bad) if a == ANOEQ { return ANOEQ } // Blank fields, padded fields, fields with non-memory // equality need special compare. - if a != AMEM || isblanksym(t1.Sym) || ispaddedfield(t1, t.Width) { + if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, f) { ret = -1 continue } @@ -236,58 +234,45 @@ func genhash(sym *Sym, t *Type) { fn.Nbody.Append(n) - // Walk the struct using memhash for runs of AMEM - // and calling specific hash functions for the others. case TSTRUCT: - var call *Node - var nx *Node - var na *Node - var hashel *Node - - t1 := t.Type - for { - first, size, next := memrun(t, t1) - t1 = next - - // Run memhash for fields up to this one. - if first != nil { - hashel = hashmem(first.Type) - - // h = hashel(&p.first, size, h) - call = Nod(OCALL, hashel, nil) + // Walk the struct using memhash for runs of AMEM + // and calling specific hash functions for the others. + for f := t.Type; f != nil; { + // Skip blank fields. + if isblanksym(f.Sym) { + f = f.Down + continue + } - nx = Nod(OXDOT, np, newname(first.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) + // Hash non-memory fields with appropriate hash function. + if algtype1(f.Type, nil) != AMEM { + hashel := hashfor(f.Type) + call := Nod(OCALL, hashel, nil) + nx := Nod(OXDOT, np, newname(f.Sym)) // TODO: fields from other packages? + na := Nod(OADDR, nx, nil) na.Etype = 1 // no escape to heap appendNodeSeqNode(&call.List, na) appendNodeSeqNode(&call.List, nh) - appendNodeSeqNode(&call.List, Nodintconst(size)) fn.Nbody.Append(Nod(OAS, nh, call)) - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - t1 = t1.Down - continue - } - if algtype1(t1.Type, nil) == AMEM { - // Our memory run might have been stopped by padding or a blank field. - // If the next field is memory-ish, it could be the start of a new run. + f = f.Down continue } - hashel = hashfor(t1.Type) - call = Nod(OCALL, hashel, nil) - nx = Nod(OXDOT, np, newname(t1.Sym)) // TODO: fields from other packages? - na = Nod(OADDR, nx, nil) + // Otherwise, hash a maximal length run of raw memory. + size, next := memrun(t, f) + + // h = hashel(&p.first, size, h) + hashel := hashmem(f.Type) + call := Nod(OCALL, hashel, nil) + nx := Nod(OXDOT, np, newname(f.Sym)) // TODO: fields from other packages? + na := Nod(OADDR, nx, nil) na.Etype = 1 // no escape to heap appendNodeSeqNode(&call.List, na) appendNodeSeqNode(&call.List, nh) + appendNodeSeqNode(&call.List, Nodintconst(size)) fn.Nbody.Append(Nod(OAS, nh, call)) - t1 = t1.Down + f = next } } @@ -438,51 +423,41 @@ func geneq(sym *Sym, t *Type) { appendNodeSeqNode(&ret.List, Nodbool(true)) fn.Nbody.Append(ret) - // Walk the struct using memequal for runs of AMEM - // and calling specific equality tests for the others. - // Skip blank-named fields. case TSTRUCT: var conjuncts []*Node - t1 := t.Type - for { - first, size, next := memrun(t, t1) - t1 = next - - // Run memequal for fields up to this one. - // TODO(rsc): All the calls to newname are wrong for - // cross-package unexported fields. - if first != nil { - if first.Down == t1 { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - } else if first.Down.Down == t1 { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - first = first.Down - if !isblanksym(first.Sym) { - conjuncts = append(conjuncts, eqfield(np, nq, newname(first.Sym))) - } - } else { - // More than two fields: use memequal. - conjuncts = append(conjuncts, eqmem(np, nq, newname(first.Sym), size)) - } - } - - if t1 == nil { - break - } - if isblanksym(t1.Sym) { - t1 = t1.Down + // Walk the struct using memequal for runs of AMEM + // and calling specific equality tests for the others. + for f := t.Type; f != nil; { + // Skip blank-named fields. + if isblanksym(f.Sym) { + f = f.Down continue } - if algtype1(t1.Type, nil) == AMEM { - // Our memory run might have been stopped by padding or a blank field. - // If the next field is memory-ish, it could be the start of a new run. + + // Compare non-memory fields with field equality. + if algtype1(f.Type, nil) != AMEM { + conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym))) + f = f.Down continue } - // Check this field, which is not just memory. - conjuncts = append(conjuncts, eqfield(np, nq, newname(t1.Sym))) - t1 = t1.Down + // Find maximal length run of memory-only fields. + size, next := memrun(t, f) + + // Run memequal on fields from f to next. + // TODO(rsc): All the calls to newname are wrong for + // cross-package unexported fields. + if f.Down == next { + conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym))) + } else if f.Down.Down == next { + conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Sym))) + conjuncts = append(conjuncts, eqfield(np, nq, newname(f.Down.Sym))) + } else { + // More than two fields: use memequal. + conjuncts = append(conjuncts, eqmem(np, nq, newname(f.Sym), size)) + } + f = next } var and *Node @@ -584,43 +559,42 @@ func eqmemfunc(size int64, type_ *Type, needsize *int) *Node { } // memrun finds runs of struct fields for which memory-only algs are appropriate. -// t is the parent struct type, and field is the field at which to start. -// first is the first field in the memory run. +// t is the parent struct type, and start is the field that starts the run. // size is the length in bytes of the memory included in the run. // next is the next field after the memory run. -func memrun(t *Type, field *Type) (first *Type, size int64, next *Type) { - var offend int64 +func memrun(t *Type, start *Type) (size int64, next *Type) { + var last *Type + next = start for { - if field == nil || algtype1(field.Type, nil) != AMEM || isblanksym(field.Sym) { + last, next = next, next.Down + if next == nil { break } - offend = field.Width + field.Type.Width - if first == nil { - first = field + // Stop run after a padded field. + if ispaddedfield(t, last) { + break } - - // If it's a memory field but it's padded, stop here. - if ispaddedfield(field, t.Width) { - field = field.Down + // Also, stop before a blank or non-memory field. + if isblanksym(next.Sym) || algtype1(next.Type, nil) != AMEM { break } - field = field.Down - } - if first != nil { - size = offend - first.Width // first.Width is offset } - return first, size, field + end := last.Width + last.Type.Width + return end - start.Width, next } -// ispaddedfield reports whether the given field -// is followed by padding. For the case where t is -// the last field, total gives the size of the enclosing struct. -func ispaddedfield(t *Type, total int64) bool { - if t.Etype != TFIELD { - Fatalf("ispaddedfield called non-field %v", t) +// ispaddedfield reports whether the given field f, assumed to be +// a field in struct t, is followed by padding. +func ispaddedfield(t *Type, f *Type) bool { + if t.Etype != TSTRUCT { + Fatalf("ispaddedfield called non-struct %v", t) + } + if f.Etype != TFIELD { + Fatalf("ispaddedfield called non-field %v", f) } - if t.Down == nil { - return t.Width+t.Type.Width != total + end := t.Width + if f.Down != nil { + end = f.Down.Width } - return t.Width+t.Type.Width != t.Down.Width + return f.Width+f.Type.Width != end }