github.com/stingnevermore/go@v0.0.0-20180120041312-3810f5bfed72/src/cmd/compile/internal/gc/alg.go (about)

     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  }