github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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 "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  	testdclstack()
   307  
   308  	// Disable safemode while compiling this code: the code we
   309  	// generate internally can refer to unsafe.Pointer.
   310  	// In this case it can happen if we need to generate an ==
   311  	// for a struct containing a reflect.Value, which itself has
   312  	// an unexported field of type unsafe.Pointer.
   313  	old_safemode := safemode
   314  	safemode = false
   315  
   316  	Disable_checknil++
   317  	funccompile(fn)
   318  	Disable_checknil--
   319  
   320  	safemode = old_safemode
   321  }
   322  
   323  func hashfor(t *Type) *Node {
   324  	var sym *Sym
   325  
   326  	switch a, _ := algtype1(t); a {
   327  	case AMEM:
   328  		Fatalf("hashfor with AMEM type")
   329  	case AINTER:
   330  		sym = Pkglookup("interhash", Runtimepkg)
   331  	case ANILINTER:
   332  		sym = Pkglookup("nilinterhash", Runtimepkg)
   333  	case ASTRING:
   334  		sym = Pkglookup("strhash", Runtimepkg)
   335  	case AFLOAT32:
   336  		sym = Pkglookup("f32hash", Runtimepkg)
   337  	case AFLOAT64:
   338  		sym = Pkglookup("f64hash", Runtimepkg)
   339  	case ACPLX64:
   340  		sym = Pkglookup("c64hash", Runtimepkg)
   341  	case ACPLX128:
   342  		sym = Pkglookup("c128hash", Runtimepkg)
   343  	default:
   344  		sym = typesymprefix(".hash", t)
   345  	}
   346  
   347  	n := newname(sym)
   348  	n.Class = PFUNC
   349  	tfn := Nod(OTFUNC, nil, nil)
   350  	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
   351  	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
   352  	tfn.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
   353  	tfn = typecheck(tfn, Etype)
   354  	n.Type = tfn.Type
   355  	return n
   356  }
   357  
   358  // geneq generates a helper function to
   359  // check equality of two values of type t.
   360  func geneq(sym *Sym, t *Type) {
   361  	if Debug['r'] != 0 {
   362  		fmt.Printf("geneq %v %v\n", sym, t)
   363  	}
   364  
   365  	lineno = 1 // less confusing than end of input
   366  	dclcontext = PEXTERN
   367  	markdcl()
   368  
   369  	// func sym(p, q *T) bool
   370  	fn := Nod(ODCLFUNC, nil, nil)
   371  
   372  	fn.Func.Nname = newname(sym)
   373  	fn.Func.Nname.Class = PFUNC
   374  	tfn := Nod(OTFUNC, nil, nil)
   375  	fn.Func.Nname.Name.Param.Ntype = tfn
   376  
   377  	n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
   378  	tfn.List.Append(n)
   379  	np := n.Left
   380  	n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
   381  	tfn.List.Append(n)
   382  	nq := n.Left
   383  	n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
   384  	tfn.Rlist.Append(n)
   385  
   386  	funchdr(fn)
   387  	fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
   388  
   389  	// geneq is only called for types that have equality but
   390  	// cannot be handled by the standard algorithms,
   391  	// so t must be either an array or a struct.
   392  	switch t.Etype {
   393  	default:
   394  		Fatalf("geneq %v", t)
   395  
   396  	case TARRAY:
   397  		// An array of pure memory would be handled by the
   398  		// standard memequal, so the element type must not be
   399  		// pure memory. Even if we unrolled the range loop,
   400  		// each iteration would be a function call, so don't bother
   401  		// unrolling.
   402  		nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
   403  
   404  		ni := newname(Lookup("i"))
   405  		ni.Type = Types[TINT]
   406  		nrange.List.Set1(ni)
   407  		nrange.Colas = true
   408  		colasdefn(nrange.List.Slice(), nrange)
   409  		ni = nrange.List.First()
   410  
   411  		// if p[i] != q[i] { return false }
   412  		nx := Nod(OINDEX, np, ni)
   413  
   414  		nx.Bounded = true
   415  		ny := Nod(OINDEX, nq, ni)
   416  		ny.Bounded = true
   417  
   418  		nif := Nod(OIF, nil, nil)
   419  		nif.Left = Nod(ONE, nx, ny)
   420  		r := Nod(ORETURN, nil, nil)
   421  		r.List.Append(Nodbool(false))
   422  		nif.Nbody.Append(r)
   423  		nrange.Nbody.Append(nif)
   424  		fn.Nbody.Append(nrange)
   425  
   426  		// return true
   427  		ret := Nod(ORETURN, nil, nil)
   428  		ret.List.Append(Nodbool(true))
   429  		fn.Nbody.Append(ret)
   430  
   431  	case TSTRUCT:
   432  		var cond *Node
   433  		and := func(n *Node) {
   434  			if cond == nil {
   435  				cond = n
   436  				return
   437  			}
   438  			cond = Nod(OANDAND, cond, n)
   439  		}
   440  
   441  		// Walk the struct using memequal for runs of AMEM
   442  		// and calling specific equality tests for the others.
   443  		for i, fields := 0, t.FieldSlice(); i < len(fields); {
   444  			f := fields[i]
   445  
   446  			// Skip blank-named fields.
   447  			if isblanksym(f.Sym) {
   448  				i++
   449  				continue
   450  			}
   451  
   452  			// Compare non-memory fields with field equality.
   453  			if !f.Type.IsRegularMemory() {
   454  				and(eqfield(np, nq, f.Sym))
   455  				i++
   456  				continue
   457  			}
   458  
   459  			// Find maximal length run of memory-only fields.
   460  			size, next := memrun(t, i)
   461  
   462  			// TODO(rsc): All the calls to newname are wrong for
   463  			// cross-package unexported fields.
   464  			if s := fields[i:next]; len(s) <= 2 {
   465  				// Two or fewer fields: use plain field equality.
   466  				for _, f := range s {
   467  					and(eqfield(np, nq, f.Sym))
   468  				}
   469  			} else {
   470  				// More than two fields: use memequal.
   471  				and(eqmem(np, nq, f.Sym, size))
   472  			}
   473  			i = next
   474  		}
   475  
   476  		if cond == nil {
   477  			cond = Nodbool(true)
   478  		}
   479  
   480  		ret := Nod(ORETURN, nil, nil)
   481  		ret.List.Append(cond)
   482  		fn.Nbody.Append(ret)
   483  	}
   484  
   485  	if Debug['r'] != 0 {
   486  		dumplist("geneq body", fn.Nbody)
   487  	}
   488  
   489  	funcbody(fn)
   490  	Curfn = fn
   491  	fn.Func.Dupok = true
   492  	fn = typecheck(fn, Etop)
   493  	typecheckslice(fn.Nbody.Slice(), Etop)
   494  	Curfn = nil
   495  	popdcl()
   496  	testdclstack()
   497  
   498  	// Disable safemode while compiling this code: the code we
   499  	// generate internally can refer to unsafe.Pointer.
   500  	// In this case it can happen if we need to generate an ==
   501  	// for a struct containing a reflect.Value, which itself has
   502  	// an unexported field of type unsafe.Pointer.
   503  	old_safemode := safemode
   504  	safemode = false
   505  
   506  	// Disable checknils while compiling this code.
   507  	// We are comparing a struct or an array,
   508  	// neither of which can be nil, and our comparisons
   509  	// are shallow.
   510  	Disable_checknil++
   511  
   512  	funccompile(fn)
   513  
   514  	safemode = old_safemode
   515  	Disable_checknil--
   516  }
   517  
   518  // eqfield returns the node
   519  // 	p.field == q.field
   520  func eqfield(p *Node, q *Node, field *Sym) *Node {
   521  	nx := NodSym(OXDOT, p, field)
   522  	ny := NodSym(OXDOT, q, field)
   523  	ne := Nod(OEQ, nx, ny)
   524  	return ne
   525  }
   526  
   527  // eqmem returns the node
   528  // 	memequal(&p.field, &q.field [, size])
   529  func eqmem(p *Node, q *Node, field *Sym, size int64) *Node {
   530  	nx := Nod(OADDR, NodSym(OXDOT, p, field), nil)
   531  	nx.Etype = 1 // does not escape
   532  	ny := Nod(OADDR, NodSym(OXDOT, q, field), nil)
   533  	ny.Etype = 1 // does not escape
   534  	nx = typecheck(nx, Erv)
   535  	ny = typecheck(ny, Erv)
   536  
   537  	fn, needsize := eqmemfunc(size, nx.Type.Elem())
   538  	call := Nod(OCALL, fn, nil)
   539  	call.List.Append(nx)
   540  	call.List.Append(ny)
   541  	if needsize {
   542  		call.List.Append(Nodintconst(size))
   543  	}
   544  
   545  	return call
   546  }
   547  
   548  func eqmemfunc(size int64, t *Type) (fn *Node, needsize bool) {
   549  	switch size {
   550  	default:
   551  		fn = syslook("memequal")
   552  		needsize = true
   553  	case 1, 2, 4, 8, 16:
   554  		buf := fmt.Sprintf("memequal%d", int(size)*8)
   555  		fn = syslook(buf)
   556  	}
   557  
   558  	fn = substArgTypes(fn, t, t)
   559  	return fn, needsize
   560  }
   561  
   562  // memrun finds runs of struct fields for which memory-only algs are appropriate.
   563  // t is the parent struct type, and start is the field index at which to start the run.
   564  // size is the length in bytes of the memory included in the run.
   565  // next is the index just after the end of the memory run.
   566  func memrun(t *Type, start int) (size int64, next int) {
   567  	next = start
   568  	for {
   569  		next++
   570  		if next == t.NumFields() {
   571  			break
   572  		}
   573  		// Stop run after a padded field.
   574  		if ispaddedfield(t, next-1) {
   575  			break
   576  		}
   577  		// Also, stop before a blank or non-memory field.
   578  		if f := t.Field(next); isblanksym(f.Sym) || !f.Type.IsRegularMemory() {
   579  			break
   580  		}
   581  	}
   582  	return t.Field(next-1).End() - t.Field(start).Offset, next
   583  }
   584  
   585  // ispaddedfield reports whether the i'th field of struct type t is followed
   586  // by padding.
   587  func ispaddedfield(t *Type, i int) bool {
   588  	if !t.IsStruct() {
   589  		Fatalf("ispaddedfield called non-struct %v", t)
   590  	}
   591  	end := t.Width
   592  	if i+1 < t.NumFields() {
   593  		end = t.Field(i + 1).Offset
   594  	}
   595  	return t.Field(i).End() != end
   596  }