github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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  	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  }