github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 TARRAY:
   131  		if t.IsSlice() {
   132  			return ANOEQ, t
   133  		}
   134  
   135  		a, bad := algtype1(t.Elem())
   136  		switch a {
   137  		case AMEM:
   138  			return AMEM, nil
   139  		case ANOEQ:
   140  			return ANOEQ, bad
   141  		}
   142  
   143  		switch t.NumElem() {
   144  		case 0:
   145  			// We checked above that the element type is comparable.
   146  			return AMEM, nil
   147  		case 1:
   148  			// Single-element array is same as its lone element.
   149  			return a, nil
   150  		}
   151  
   152  		return ASPECIAL, nil
   153  
   154  	case TSTRUCT:
   155  		fields := t.FieldSlice()
   156  
   157  		// One-field struct is same as that one field alone.
   158  		if len(fields) == 1 && !isblanksym(fields[0].Sym) {
   159  			return algtype1(fields[0].Type)
   160  		}
   161  
   162  		ret := AMEM
   163  		for i, f := range fields {
   164  			// All fields must be comparable.
   165  			a, bad := algtype1(f.Type)
   166  			if a == ANOEQ {
   167  				return ANOEQ, bad
   168  			}
   169  
   170  			// Blank fields, padded fields, fields with non-memory
   171  			// equality need special compare.
   172  			if a != AMEM || isblanksym(f.Sym) || ispaddedfield(t, i) {
   173  				ret = ASPECIAL
   174  			}
   175  		}
   176  
   177  		return ret, nil
   178  	}
   179  
   180  	Fatalf("algtype1: unexpected type %v", t)
   181  	return 0, nil
   182  }
   183  
   184  // Generate a helper function to compute the hash of a value of type t.
   185  func genhash(sym *Sym, t *Type) {
   186  	if Debug['r'] != 0 {
   187  		fmt.Printf("genhash %v %v\n", sym, t)
   188  	}
   189  
   190  	lineno = 1 // less confusing than end of input
   191  	dclcontext = PEXTERN
   192  	markdcl()
   193  
   194  	// func sym(p *T, h uintptr) uintptr
   195  	fn := Nod(ODCLFUNC, nil, nil)
   196  
   197  	fn.Func.Nname = newname(sym)
   198  	fn.Func.Nname.Class = PFUNC
   199  	tfn := Nod(OTFUNC, nil, nil)
   200  	fn.Func.Nname.Name.Param.Ntype = tfn
   201  
   202  	n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
   203  	tfn.List.Append(n)
   204  	np := n.Left
   205  	n = Nod(ODCLFIELD, newname(Lookup("h")), typenod(Types[TUINTPTR]))
   206  	tfn.List.Append(n)
   207  	nh := n.Left
   208  	n = Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])) // return value
   209  	tfn.Rlist.Append(n)
   210  
   211  	funchdr(fn)
   212  	fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
   213  
   214  	// genhash is only called for types that have equality but
   215  	// cannot be handled by the standard algorithms,
   216  	// so t must be either an array or a struct.
   217  	switch t.Etype {
   218  	default:
   219  		Fatalf("genhash %v", t)
   220  
   221  	case TARRAY:
   222  		if t.IsSlice() {
   223  			Fatalf("genhash %v", t)
   224  		}
   225  
   226  		// An array of pure memory would be handled by the
   227  		// standard algorithm, so the element type must not be
   228  		// pure memory.
   229  		hashel := hashfor(t.Elem())
   230  
   231  		n := Nod(ORANGE, nil, Nod(OIND, np, nil))
   232  		ni := newname(Lookup("i"))
   233  		ni.Type = Types[TINT]
   234  		n.List.Set1(ni)
   235  		n.Colas = true
   236  		colasdefn(n.List.Slice(), n)
   237  		ni = n.List.First()
   238  
   239  		// h = hashel(&p[i], h)
   240  		call := Nod(OCALL, hashel, nil)
   241  
   242  		nx := Nod(OINDEX, np, ni)
   243  		nx.Bounded = true
   244  		na := Nod(OADDR, nx, nil)
   245  		na.Etype = 1 // no escape to heap
   246  		call.List.Append(na)
   247  		call.List.Append(nh)
   248  		n.Nbody.Append(Nod(OAS, nh, call))
   249  
   250  		fn.Nbody.Append(n)
   251  
   252  	case TSTRUCT:
   253  		// Walk the struct using memhash for runs of AMEM
   254  		// and calling specific hash functions for the others.
   255  		for i, fields := 0, t.FieldSlice(); i < len(fields); {
   256  			f := fields[i]
   257  
   258  			// Skip blank fields.
   259  			if isblanksym(f.Sym) {
   260  				i++
   261  				continue
   262  			}
   263  
   264  			// Hash non-memory fields with appropriate hash function.
   265  			if !f.Type.IsRegularMemory() {
   266  				hashel := hashfor(f.Type)
   267  				call := Nod(OCALL, hashel, nil)
   268  				nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
   269  				na := Nod(OADDR, nx, nil)
   270  				na.Etype = 1 // no escape to heap
   271  				call.List.Append(na)
   272  				call.List.Append(nh)
   273  				fn.Nbody.Append(Nod(OAS, nh, call))
   274  				i++
   275  				continue
   276  			}
   277  
   278  			// Otherwise, hash a maximal length run of raw memory.
   279  			size, next := memrun(t, i)
   280  
   281  			// h = hashel(&p.first, size, h)
   282  			hashel := hashmem(f.Type)
   283  			call := Nod(OCALL, hashel, nil)
   284  			nx := NodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
   285  			na := Nod(OADDR, nx, nil)
   286  			na.Etype = 1 // no escape to heap
   287  			call.List.Append(na)
   288  			call.List.Append(nh)
   289  			call.List.Append(Nodintconst(size))
   290  			fn.Nbody.Append(Nod(OAS, nh, call))
   291  
   292  			i = next
   293  		}
   294  	}
   295  
   296  	r := Nod(ORETURN, nil, nil)
   297  	r.List.Append(nh)
   298  	fn.Nbody.Append(r)
   299  
   300  	if Debug['r'] != 0 {
   301  		dumplist("genhash body", fn.Nbody)
   302  	}
   303  
   304  	funcbody(fn)
   305  	Curfn = fn
   306  	fn.Func.Dupok = true
   307  	fn = typecheck(fn, Etop)
   308  	typecheckslice(fn.Nbody.Slice(), Etop)
   309  	Curfn = nil
   310  	popdcl()
   311  	testdclstack()
   312  
   313  	// Disable safemode while compiling this code: the code we
   314  	// generate internally can refer to unsafe.Pointer.
   315  	// In this case it can happen if we need to generate an ==
   316  	// for a struct containing a reflect.Value, which itself has
   317  	// an unexported field of type unsafe.Pointer.
   318  	old_safemode := safemode
   319  
   320  	safemode = 0
   321  	Disable_checknil++
   322  	funccompile(fn)
   323  	Disable_checknil--
   324  	safemode = old_safemode
   325  }
   326  
   327  func hashfor(t *Type) *Node {
   328  	var sym *Sym
   329  
   330  	switch a, _ := algtype1(t); a {
   331  	case AMEM:
   332  		Fatalf("hashfor with AMEM type")
   333  	case AINTER:
   334  		sym = Pkglookup("interhash", Runtimepkg)
   335  	case ANILINTER:
   336  		sym = Pkglookup("nilinterhash", Runtimepkg)
   337  	case ASTRING:
   338  		sym = Pkglookup("strhash", Runtimepkg)
   339  	case AFLOAT32:
   340  		sym = Pkglookup("f32hash", Runtimepkg)
   341  	case AFLOAT64:
   342  		sym = Pkglookup("f64hash", Runtimepkg)
   343  	case ACPLX64:
   344  		sym = Pkglookup("c64hash", Runtimepkg)
   345  	case ACPLX128:
   346  		sym = Pkglookup("c128hash", Runtimepkg)
   347  	default:
   348  		sym = typesymprefix(".hash", t)
   349  	}
   350  
   351  	n := newname(sym)
   352  	n.Class = PFUNC
   353  	tfn := Nod(OTFUNC, nil, nil)
   354  	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
   355  	tfn.List.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
   356  	tfn.Rlist.Append(Nod(ODCLFIELD, nil, typenod(Types[TUINTPTR])))
   357  	tfn = typecheck(tfn, Etype)
   358  	n.Type = tfn.Type
   359  	return n
   360  }
   361  
   362  // geneq generates a helper function to
   363  // check equality of two values of type t.
   364  func geneq(sym *Sym, t *Type) {
   365  	if Debug['r'] != 0 {
   366  		fmt.Printf("geneq %v %v\n", sym, t)
   367  	}
   368  
   369  	lineno = 1 // less confusing than end of input
   370  	dclcontext = PEXTERN
   371  	markdcl()
   372  
   373  	// func sym(p, q *T) bool
   374  	fn := Nod(ODCLFUNC, nil, nil)
   375  
   376  	fn.Func.Nname = newname(sym)
   377  	fn.Func.Nname.Class = PFUNC
   378  	tfn := Nod(OTFUNC, nil, nil)
   379  	fn.Func.Nname.Name.Param.Ntype = tfn
   380  
   381  	n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t)))
   382  	tfn.List.Append(n)
   383  	np := n.Left
   384  	n = Nod(ODCLFIELD, newname(Lookup("q")), typenod(Ptrto(t)))
   385  	tfn.List.Append(n)
   386  	nq := n.Left
   387  	n = Nod(ODCLFIELD, nil, typenod(Types[TBOOL]))
   388  	tfn.Rlist.Append(n)
   389  
   390  	funchdr(fn)
   391  	fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
   392  
   393  	// geneq is only called for types that have equality but
   394  	// cannot be handled by the standard algorithms,
   395  	// so t must be either an array or a struct.
   396  	switch t.Etype {
   397  	default:
   398  		Fatalf("geneq %v", t)
   399  
   400  	case TARRAY:
   401  		if t.IsSlice() {
   402  			Fatalf("geneq %v", t)
   403  		}
   404  
   405  		// An array of pure memory would be handled by the
   406  		// standard memequal, so the element type must not be
   407  		// pure memory. Even if we unrolled the range loop,
   408  		// each iteration would be a function call, so don't bother
   409  		// unrolling.
   410  		nrange := Nod(ORANGE, nil, Nod(OIND, np, nil))
   411  
   412  		ni := newname(Lookup("i"))
   413  		ni.Type = Types[TINT]
   414  		nrange.List.Set1(ni)
   415  		nrange.Colas = true
   416  		colasdefn(nrange.List.Slice(), nrange)
   417  		ni = nrange.List.First()
   418  
   419  		// if p[i] != q[i] { return false }
   420  		nx := Nod(OINDEX, np, ni)
   421  
   422  		nx.Bounded = true
   423  		ny := Nod(OINDEX, nq, ni)
   424  		ny.Bounded = true
   425  
   426  		nif := Nod(OIF, nil, nil)
   427  		nif.Left = Nod(ONE, nx, ny)
   428  		r := Nod(ORETURN, nil, nil)
   429  		r.List.Append(Nodbool(false))
   430  		nif.Nbody.Append(r)
   431  		nrange.Nbody.Append(nif)
   432  		fn.Nbody.Append(nrange)
   433  
   434  		// return true
   435  		ret := Nod(ORETURN, nil, nil)
   436  		ret.List.Append(Nodbool(true))
   437  		fn.Nbody.Append(ret)
   438  
   439  	case TSTRUCT:
   440  		var cond *Node
   441  		and := func(n *Node) {
   442  			if cond == nil {
   443  				cond = n
   444  				return
   445  			}
   446  			cond = Nod(OANDAND, cond, n)
   447  		}
   448  
   449  		// Walk the struct using memequal for runs of AMEM
   450  		// and calling specific equality tests for the others.
   451  		for i, fields := 0, t.FieldSlice(); i < len(fields); {
   452  			f := fields[i]
   453  
   454  			// Skip blank-named fields.
   455  			if isblanksym(f.Sym) {
   456  				i++
   457  				continue
   458  			}
   459  
   460  			// Compare non-memory fields with field equality.
   461  			if !f.Type.IsRegularMemory() {
   462  				and(eqfield(np, nq, f.Sym))
   463  				i++
   464  				continue
   465  			}
   466  
   467  			// Find maximal length run of memory-only fields.
   468  			size, next := memrun(t, i)
   469  
   470  			// TODO(rsc): All the calls to newname are wrong for
   471  			// cross-package unexported fields.
   472  			if s := fields[i:next]; len(s) <= 2 {
   473  				// Two or fewer fields: use plain field equality.
   474  				for _, f := range s {
   475  					and(eqfield(np, nq, f.Sym))
   476  				}
   477  			} else {
   478  				// More than two fields: use memequal.
   479  				and(eqmem(np, nq, f.Sym, size))
   480  			}
   481  			i = next
   482  		}
   483  
   484  		if cond == nil {
   485  			cond = Nodbool(true)
   486  		}
   487  
   488  		ret := Nod(ORETURN, nil, nil)
   489  		ret.List.Append(cond)
   490  		fn.Nbody.Append(ret)
   491  	}
   492  
   493  	if Debug['r'] != 0 {
   494  		dumplist("geneq body", fn.Nbody)
   495  	}
   496  
   497  	funcbody(fn)
   498  	Curfn = fn
   499  	fn.Func.Dupok = true
   500  	fn = typecheck(fn, Etop)
   501  	typecheckslice(fn.Nbody.Slice(), Etop)
   502  	Curfn = nil
   503  	popdcl()
   504  	testdclstack()
   505  
   506  	// Disable safemode while compiling this code: the code we
   507  	// generate internally can refer to unsafe.Pointer.
   508  	// In this case it can happen if we need to generate an ==
   509  	// for a struct containing a reflect.Value, which itself has
   510  	// an unexported field of type unsafe.Pointer.
   511  	old_safemode := safemode
   512  	safemode = 0
   513  
   514  	// Disable checknils while compiling this code.
   515  	// We are comparing a struct or an array,
   516  	// neither of which can be nil, and our comparisons
   517  	// are shallow.
   518  	Disable_checknil++
   519  
   520  	funccompile(fn)
   521  
   522  	safemode = old_safemode
   523  	Disable_checknil--
   524  }
   525  
   526  // eqfield returns the node
   527  // 	p.field == q.field
   528  func eqfield(p *Node, q *Node, field *Sym) *Node {
   529  	nx := NodSym(OXDOT, p, field)
   530  	ny := NodSym(OXDOT, q, field)
   531  	ne := Nod(OEQ, nx, ny)
   532  	return ne
   533  }
   534  
   535  // eqmem returns the node
   536  // 	memequal(&p.field, &q.field [, size])
   537  func eqmem(p *Node, q *Node, field *Sym, size int64) *Node {
   538  	nx := Nod(OADDR, NodSym(OXDOT, p, field), nil)
   539  	nx.Etype = 1 // does not escape
   540  	ny := Nod(OADDR, NodSym(OXDOT, q, field), nil)
   541  	ny.Etype = 1 // does not escape
   542  	nx = typecheck(nx, Erv)
   543  	ny = typecheck(ny, Erv)
   544  
   545  	fn, needsize := eqmemfunc(size, nx.Type.Elem())
   546  	call := Nod(OCALL, fn, nil)
   547  	call.List.Append(nx)
   548  	call.List.Append(ny)
   549  	if needsize {
   550  		call.List.Append(Nodintconst(size))
   551  	}
   552  
   553  	return call
   554  }
   555  
   556  func eqmemfunc(size int64, t *Type) (fn *Node, needsize bool) {
   557  	switch size {
   558  	default:
   559  		fn = syslook("memequal")
   560  		needsize = true
   561  	case 1, 2, 4, 8, 16:
   562  		buf := fmt.Sprintf("memequal%d", int(size)*8)
   563  		fn = syslook(buf)
   564  	}
   565  
   566  	fn = substArgTypes(fn, t, t)
   567  	return fn, needsize
   568  }
   569  
   570  // memrun finds runs of struct fields for which memory-only algs are appropriate.
   571  // t is the parent struct type, and start is the field index at which to start the run.
   572  // size is the length in bytes of the memory included in the run.
   573  // next is the index just after the end of the memory run.
   574  func memrun(t *Type, start int) (size int64, next int) {
   575  	next = start
   576  	for {
   577  		next++
   578  		if next == t.NumFields() {
   579  			break
   580  		}
   581  		// Stop run after a padded field.
   582  		if ispaddedfield(t, next-1) {
   583  			break
   584  		}
   585  		// Also, stop before a blank or non-memory field.
   586  		if f := t.Field(next); isblanksym(f.Sym) || !f.Type.IsRegularMemory() {
   587  			break
   588  		}
   589  	}
   590  	return t.Field(next-1).End() - t.Field(start).Offset, next
   591  }
   592  
   593  // ispaddedfield reports whether the i'th field of struct type t is followed
   594  // by padding.
   595  func ispaddedfield(t *Type, i int) bool {
   596  	if !t.IsStruct() {
   597  		Fatalf("ispaddedfield called non-struct %v", t)
   598  	}
   599  	end := t.Width
   600  	if i+1 < t.NumFields() {
   601  		end = t.Field(i + 1).Offset
   602  	}
   603  	return t.Field(i).End() != end
   604  }