Home | History | Annotate | Download | only in gc
      1 // Copyright 2016 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package gc
      6 
      7 import "fmt"
      8 
      9 // AlgKind describes the kind of algorithms used for comparing and
     10 // hashing a Type.
     11 type AlgKind int
     12 
     13 const (
     14 	// These values are known by runtime.
     15 	ANOEQ AlgKind = iota
     16 	AMEM0
     17 	AMEM8
     18 	AMEM16
     19 	AMEM32
     20 	AMEM64
     21 	AMEM128
     22 	ASTRING
     23 	AINTER
     24 	ANILINTER
     25 	AFLOAT32
     26 	AFLOAT64
     27 	ACPLX64
     28 	ACPLX128
     29 
     30 	// Type can be compared/hashed as regular memory.
     31 	AMEM AlgKind = 100
     32 
     33 	// Type needs special comparison/hashing functions.
     34 	ASPECIAL AlgKind = -1
     35 )
     36 
     37 // IsComparable reports whether t is a comparable type.
     38 func (t *Type) IsComparable() bool {
     39 	a, _ := algtype1(t)
     40 	return a != ANOEQ
     41 }
     42 
     43 // IsRegularMemory reports whether t can be compared/hashed as regular memory.
     44 func (t *Type) IsRegularMemory() bool {
     45 	a, _ := algtype1(t)
     46 	return a == AMEM
     47 }
     48 
     49 // IncomparableField returns an incomparable Field of struct Type t, if any.
     50 func (t *Type) IncomparableField() *Field {
     51 	for _, f := range t.FieldSlice() {
     52 		if !f.Type.IsComparable() {
     53 			return f
     54 		}
     55 	}
     56 	return nil
     57 }
     58 
     59 // algtype is like algtype1, except it returns the fixed-width AMEMxx variants
     60 // instead of the general AMEM kind when possible.
     61 func algtype(t *Type) AlgKind {
     62 	a, _ := algtype1(t)
     63 	if a == AMEM {
     64 		switch t.Width {
     65 		case 0:
     66 			return AMEM0
     67 		case 1:
     68 			return AMEM8
     69 		case 2:
     70 			return AMEM16
     71 		case 4:
     72 			return AMEM32
     73 		case 8:
     74 			return AMEM64
     75 		case 16:
     76 			return AMEM128
     77 		}
     78 	}
     79 
     80 	return a
     81 }
     82 
     83 // algtype1 returns the AlgKind used for comparing and hashing Type t.
     84 // If it returns ANOEQ, it also returns the component type of t that
     85 // makes it incomparable.
     86 func algtype1(t *Type) (AlgKind, *Type) {
     87 	if t.Broke {
     88 		return AMEM, nil
     89 	}
     90 	if t.Noalg {
     91 		return ANOEQ, t
     92 	}
     93 
     94 	switch t.Etype {
     95 	case TANY, TFORW:
     96 		// will be defined later.
     97 		return ANOEQ, t
     98 
     99 	case TINT8, TUINT8, TINT16, TUINT16,
    100 		TINT32, TUINT32, TINT64, TUINT64,
    101 		TINT, TUINT, TUINTPTR,
    102 		TBOOL, TPTR32, TPTR64,
    103 		TCHAN, TUNSAFEPTR:
    104 		return AMEM, nil
    105 
    106 	case TFUNC, TMAP:
    107 		return ANOEQ, t
    108 
    109 	case TFLOAT32:
    110 		return AFLOAT32, nil
    111 
    112 	case TFLOAT64:
    113 		return AFLOAT64, nil
    114 
    115 	case TCOMPLEX64:
    116 		return ACPLX64, nil
    117 
    118 	case TCOMPLEX128:
    119 		return ACPLX128, nil
    120 
    121 	case TSTRING:
    122 		return ASTRING, nil
    123 
    124 	case TINTER:
    125 		if t.IsEmptyInterface() {
    126 			return ANILINTER, nil
    127 		}
    128 		return AINTER, nil
    129 
    130 	case TSLICE:
    131 		return ANOEQ, t
    132 
    133 	case TARRAY:
    134 		a, bad := algtype1(t.Elem())
    135 		switch a {
    136 		case AMEM:
    137 			return AMEM, nil
    138 		case ANOEQ:
    139 			return ANOEQ, bad
    140 		}
    141 
    142 		switch t.NumElem() {
    143 		case 0:
    144 			// We checked above that the element type is comparable.
    145 			return AMEM, nil
    146 		case 1:
    147 			// Single-element array is same as its lone element.
    148 			return a, nil
    149 		}
    150 
    151 		return ASPECIAL, nil
    152 
    153 	case TSTRUCT:
    154 		fields := t.FieldSlice()
    155 
    156 		// One-field struct is same as that one field alone.
    157 		if len(fields) == 1 && !isblanksym(fields[0].Sym) {
    158 			return algtype1(fields[0].Type)
    159 		}
    160 
    161 		ret := AMEM
    162 		for i, f := range fields {
    163 			// All fields must be comparable.
    164 			a, bad := algtype1(f.Type)
    165 			if a == ANOEQ {
    166 				return ANOEQ, bad
    167 			}
    168 
    169 			// Blank fields, padded fields, fields with non-memory
    170 			// equality need special compare.
    171 			if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, i) {
    172 				ret = ASPECIAL
    173 			}
    174 		}
    175 
    176 		return ret, nil
    177 	}
    178 
    179 	Fatalf("algtype1: unexpected type %v", t)
    180 	return 0, nil
    181 }
    182 
    183 // Generate a helper function to compute the hash of a value of type t.
    184 func genhash(sym *Sym, t *Type) {
    185 	if Debug['r'] != 0 {
    186 		fmt.Printf("genhash %v %v\n", sym, t)
    187 	}
    188 
    189 	lineno = 1 // less confusing than end of input
    190 	dclcontext = PEXTERN
    191 	markdcl()
    192 
    193 	// func sym(p *T, h uintptr) uintptr
    194 	fn := nod(ODCLFUNC, nil, nil)
    195 
    196 	fn.Func.Nname = newname(sym)
    197 	fn.Func.Nname.Class = PFUNC
    198 	tfn := nod(OTFUNC, nil, nil)
    199 	fn.Func.Nname.Name.Param.Ntype = tfn
    200 
    201 	n := nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)))
    202 	tfn.List.Append(n)
    203 	np := n.Left
    204 	n = nod(ODCLFIELD, newname(lookup("h")), typenod(Types[TUINTPTR]))
    205 	tfn.List.Append(n)
    206 	nh := n.Left
    207 	n = nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
    208 	tfn.Rlist.Append(n)
    209 
    210 	funchdr(fn)
    211 	fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
    212 
    213 	// genhash is only called for types that have equality but
    214 	// cannot be handled by the standard algorithms,
    215 	// so t must be either an array or a struct.
    216 	switch t.Etype {
    217 	default:
    218 		Fatalf("genhash %v", t)
    219 
    220 	case TARRAY:
    221 		// An array of pure memory would be handled by the
    222 		// standard algorithm, so the element type must not be
    223 		// pure memory.
    224 		hashel := hashfor(t.Elem())
    225 
    226 		n := nod(ORANGE, nil, nod(OIND, np, nil))
    227 		ni := newname(lookup("i"))
    228 		ni.Type = Types[TINT]
    229 		n.List.Set1(ni)
    230 		n.Colas = true
    231 		colasdefn(n.List.Slice(), n)
    232 		ni = n.List.First()
    233 
    234 		// h = hashel(&p[i], h)
    235 		call := nod(OCALL, hashel, nil)
    236 
    237 		nx := nod(OINDEX, np, ni)
    238 		nx.Bounded = true
    239 		na := nod(OADDR, nx, nil)
    240 		na.Etype = 1 // no escape to heap
    241 		call.List.Append(na)
    242 		call.List.Append(nh)
    243 		n.Nbody.Append(nod(OAS, nh, call))
    244 
    245 		fn.Nbody.Append(n)
    246 
    247 	case TSTRUCT:
    248 		// Walk the struct using memhash for runs of AMEM
    249 		// and calling specific hash functions for the others.
    250 		for i, fields := 0, t.FieldSlice(); i < len(fields); {
    251 			f := fields[i]
    252 
    253 			// Skip blank fields.
    254 			if isblanksym(f.Sym) {
    255 				i++
    256 				continue
    257 			}
    258 
    259 			// Hash non-memory fields with appropriate hash function.
    260 			if !f.Type.IsRegularMemory() {
    261 				hashel := hashfor(f.Type)
    262 				call := nod(OCALL, hashel, nil)
    263 				nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
    264 				na := nod(OADDR, nx, nil)
    265 				na.Etype = 1 // no escape to heap
    266 				call.List.Append(na)
    267 				call.List.Append(nh)
    268 				fn.Nbody.Append(nod(OAS, nh, call))
    269 				i++
    270 				continue
    271 			}
    272 
    273 			// Otherwise, hash a maximal length run of raw memory.
    274 			size, next := memrun(t, i)
    275 
    276 			// h = hashel(&p.first, size, h)
    277 			hashel := hashmem(f.Type)
    278 			call := nod(OCALL, hashel, nil)
    279 			nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
    280 			na := nod(OADDR, nx, nil)
    281 			na.Etype = 1 // no escape to heap
    282 			call.List.Append(na)
    283 			call.List.Append(nh)
    284 			call.List.Append(nodintconst(size))
    285 			fn.Nbody.Append(nod(OAS, nh, call))
    286 
    287 			i = next
    288 		}
    289 	}
    290 
    291 	r := nod(ORETURN, nil, nil)
    292 	r.List.Append(nh)
    293 	fn.Nbody.Append(r)
    294 
    295 	if Debug['r'] != 0 {
    296 		dumplist("genhash body", fn.Nbody)
    297 	}
    298 
    299 	funcbody(fn)
    300 	Curfn = fn
    301 	fn.Func.Dupok = true
    302 	fn = typecheck(fn, Etop)
    303 	typecheckslice(fn.Nbody.Slice(), Etop)
    304 	Curfn = nil
    305 	popdcl()
    306 	if debug_dclstack != 0 {
    307 		testdclstack()
    308 	}
    309 
    310 	// Disable safemode while compiling this code: the code we
    311 	// generate internally can refer to unsafe.Pointer.
    312 	// In this case it can happen if we need to generate an ==
    313 	// for a struct containing a reflect.Value, which itself has
    314 	// an unexported field of type unsafe.Pointer.
    315 	old_safemode := safemode
    316 	safemode = false
    317 
    318 	disable_checknil++
    319 	funccompile(fn)
    320 	disable_checknil--
    321 
    322 	safemode = old_safemode
    323 }
    324 
    325 func hashfor(t *Type) *Node {
    326 	var sym *Sym
    327 
    328 	switch a, _ := algtype1(t); a {
    329 	case AMEM:
    330 		Fatalf("hashfor with AMEM type")
    331 	case AINTER:
    332 		sym = Pkglookup("interhash", Runtimepkg)
    333 	case ANILINTER:
    334 		sym = Pkglookup("nilinterhash", Runtimepkg)
    335 	case ASTRING:
    336 		sym = Pkglookup("strhash", Runtimepkg)
    337 	case AFLOAT32:
    338 		sym = Pkglookup("f32hash", Runtimepkg)
    339 	case AFLOAT64:
    340 		sym = Pkglookup("f64hash", Runtimepkg)
    341 	case ACPLX64:
    342 		sym = Pkglookup("c64hash", Runtimepkg)
    343 	case ACPLX128:
    344 		sym = Pkglookup("c128hash", Runtimepkg)
    345 	default:
    346 		sym = typesymprefix(".hash", t)
    347 	}
    348 
    349 	n := newname(sym)
    350 	n.Class = PFUNC
    351 	tfn := nod(OTFUNC, nil, nil)
    352 	tfn.List.Append(nod(ODCLFIELD, nil, typenod(ptrto(t))))
    353 	tfn.List.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
    354 	tfn.Rlist.Append(nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
    355 	tfn = typecheck(tfn, Etype)
    356 	n.Type = tfn.Type
    357 	return n
    358 }
    359 
    360 // geneq generates a helper function to
    361 // check equality of two values of type t.
    362 func geneq(sym *Sym, t *Type) {
    363 	if Debug['r'] != 0 {
    364 		fmt.Printf("geneq %v %v\n", sym, t)
    365 	}
    366 
    367 	lineno = 1 // less confusing than end of input
    368 	dclcontext = PEXTERN
    369 	markdcl()
    370 
    371 	// func sym(p, q *T) bool
    372 	fn := nod(ODCLFUNC, nil, nil)
    373 
    374 	fn.Func.Nname = newname(sym)
    375 	fn.Func.Nname.Class = PFUNC
    376 	tfn := nod(OTFUNC, nil, nil)
    377 	fn.Func.Nname.Name.Param.Ntype = tfn
    378 
    379 	n := nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)))
    380 	tfn.List.Append(n)
    381 	np := n.Left
    382 	n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)))
    383 	tfn.List.Append(n)
    384 	nq := n.Left
    385 	n = nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
    386 	tfn.Rlist.Append(n)
    387 
    388 	funchdr(fn)
    389 	fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
    390 
    391 	// geneq is only called for types that have equality but
    392 	// cannot be handled by the standard algorithms,
    393 	// so t must be either an array or a struct.
    394 	switch t.Etype {
    395 	default:
    396 		Fatalf("geneq %v", t)
    397 
    398 	case TARRAY:
    399 		// An array of pure memory would be handled by the
    400 		// standard memequal, so the element type must not be
    401 		// pure memory. Even if we unrolled the range loop,
    402 		// each iteration would be a function call, so don't bother
    403 		// unrolling.
    404 		nrange := nod(ORANGE, nil, nod(OIND, np, nil))
    405 
    406 		ni := newname(lookup("i"))
    407 		ni.Type = Types[TINT]
    408 		nrange.List.Set1(ni)
    409 		nrange.Colas = true
    410 		colasdefn(nrange.List.Slice(), nrange)
    411 		ni = nrange.List.First()
    412 
    413 		// if p[i] != q[i] { return false }
    414 		nx := nod(OINDEX, np, ni)
    415 
    416 		nx.Bounded = true
    417 		ny := nod(OINDEX, nq, ni)
    418 		ny.Bounded = true
    419 
    420 		nif := nod(OIF, nil, nil)
    421 		nif.Left = nod(ONE, nx, ny)
    422 		r := nod(ORETURN, nil, nil)
    423 		r.List.Append(nodbool(false))
    424 		nif.Nbody.Append(r)
    425 		nrange.Nbody.Append(nif)
    426 		fn.Nbody.Append(nrange)
    427 
    428 		// return true
    429 		ret := nod(ORETURN, nil, nil)
    430 		ret.List.Append(nodbool(true))
    431 		fn.Nbody.Append(ret)
    432 
    433 	case TSTRUCT:
    434 		var cond *Node
    435 		and := func(n *Node) {
    436 			if cond == nil {
    437 				cond = n
    438 				return
    439 			}
    440 			cond = nod(OANDAND, cond, n)
    441 		}
    442 
    443 		// Walk the struct using memequal for runs of AMEM
    444 		// and calling specific equality tests for the others.
    445 		for i, fields := 0, t.FieldSlice(); i < len(fields); {
    446 			f := fields[i]
    447 
    448 			// Skip blank-named fields.
    449 			if isblanksym(f.Sym) {
    450 				i++
    451 				continue
    452 			}
    453 
    454 			// Compare non-memory fields with field equality.
    455 			if !f.Type.IsRegularMemory() {
    456 				and(eqfield(np, nq, f.Sym))
    457 				i++
    458 				continue
    459 			}
    460 
    461 			// Find maximal length run of memory-only fields.
    462 			size, next := memrun(t, i)
    463 
    464 			// TODO(rsc): All the calls to newname are wrong for
    465 			// cross-package unexported fields.
    466 			if s := fields[i:next]; len(s) <= 2 {
    467 				// Two or fewer fields: use plain field equality.
    468 				for _, f := range s {
    469 					and(eqfield(np, nq, f.Sym))
    470 				}
    471 			} else {
    472 				// More than two fields: use memequal.
    473 				and(eqmem(np, nq, f.Sym, size))
    474 			}
    475 			i = next
    476 		}
    477 
    478 		if cond == nil {
    479 			cond = nodbool(true)
    480 		}
    481 
    482 		ret := nod(ORETURN, nil, nil)
    483 		ret.List.Append(cond)
    484 		fn.Nbody.Append(ret)
    485 	}
    486 
    487 	if Debug['r'] != 0 {
    488 		dumplist("geneq body", fn.Nbody)
    489 	}
    490 
    491 	funcbody(fn)
    492 	Curfn = fn
    493 	fn.Func.Dupok = true
    494 	fn = typecheck(fn, Etop)
    495 	typecheckslice(fn.Nbody.Slice(), Etop)
    496 	Curfn = nil
    497 	popdcl()
    498 	if debug_dclstack != 0 {
    499 		testdclstack()
    500 	}
    501 
    502 	// Disable safemode while compiling this code: the code we
    503 	// generate internally can refer to unsafe.Pointer.
    504 	// In this case it can happen if we need to generate an ==
    505 	// for a struct containing a reflect.Value, which itself has
    506 	// an unexported field of type unsafe.Pointer.
    507 	old_safemode := safemode
    508 	safemode = false
    509 
    510 	// Disable checknils while compiling this code.
    511 	// We are comparing a struct or an array,
    512 	// neither of which can be nil, and our comparisons
    513 	// are shallow.
    514 	disable_checknil++
    515 
    516 	funccompile(fn)
    517 
    518 	safemode = old_safemode
    519 	disable_checknil--
    520 }
    521 
    522 // eqfield returns the node
    523 // 	p.field == q.field
    524 func eqfield(p *Node, q *Node, field *Sym) *Node {
    525 	nx := nodSym(OXDOT, p, field)
    526 	ny := nodSym(OXDOT, q, field)
    527 	ne := nod(OEQ, nx, ny)
    528 	return ne
    529 }
    530 
    531 // eqmem returns the node
    532 // 	memequal(&p.field, &q.field [, size])
    533 func eqmem(p *Node, q *Node, field *Sym, size int64) *Node {
    534 	nx := nod(OADDR, nodSym(OXDOT, p, field), nil)
    535 	nx.Etype = 1 // does not escape
    536 	ny := nod(OADDR, nodSym(OXDOT, q, field), nil)
    537 	ny.Etype = 1 // does not escape
    538 	nx = typecheck(nx, Erv)
    539 	ny = typecheck(ny, Erv)
    540 
    541 	fn, needsize := eqmemfunc(size, nx.Type.Elem())
    542 	call := nod(OCALL, fn, nil)
    543 	call.List.Append(nx)
    544 	call.List.Append(ny)
    545 	if needsize {
    546 		call.List.Append(nodintconst(size))
    547 	}
    548 
    549 	return call
    550 }
    551 
    552 func eqmemfunc(size int64, t *Type) (fn *Node, needsize bool) {
    553 	switch size {
    554 	default:
    555 		fn = syslook("memequal")
    556 		needsize = true
    557 	case 1, 2, 4, 8, 16:
    558 		buf := fmt.Sprintf("memequal%d", int(size)*8)
    559 		fn = syslook(buf)
    560 	}
    561 
    562 	fn = substArgTypes(fn, t, t)
    563 	return fn, needsize
    564 }
    565 
    566 // memrun finds runs of struct fields for which memory-only algs are appropriate.
    567 // t is the parent struct type, and start is the field index at which to start the run.
    568 // size is the length in bytes of the memory included in the run.
    569 // next is the index just after the end of the memory run.
    570 func memrun(t *Type, start int) (size int64, next int) {
    571 	next = start
    572 	for {
    573 		next++
    574 		if next == t.NumFields() {
    575 			break
    576 		}
    577 		// Stop run after a padded field.
    578 		if ispaddedfield(t, next-1) {
    579 			break
    580 		}
    581 		// Also, stop before a blank or non-memory field.
    582 		if f := t.Field(next); isblanksym(f.Sym) || !f.Type.IsRegularMemory() {
    583 			break
    584 		}
    585 	}
    586 	return t.Field(next-1).End() - t.Field(start).Offset, next
    587 }
    588 
    589 // ispaddedfield reports whether the i'th field of struct type t is followed
    590 // by padding.
    591 func ispaddedfield(t *Type, i int) bool {
    592 	if !t.IsStruct() {
    593 		Fatalf("ispaddedfield called non-struct %v", t)
    594 	}
    595 	end := t.Width
    596 	if i+1 < t.NumFields() {
    597 		end = t.Field(i + 1).Offset
    598 	}
    599 	return t.Field(i).End() != end
    600 }
    601