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