github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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, TPTR,
   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  
   195  	// func sym(p *T, h uintptr) uintptr
   196  	tfn := nod(OTFUNC, nil, nil)
   197  	tfn.List.Set2(
   198  		namedfield("p", types.NewPtr(t)),
   199  		namedfield("h", types.Types[TUINTPTR]),
   200  	)
   201  	tfn.Rlist.Set1(anonfield(types.Types[TUINTPTR]))
   202  
   203  	fn := dclfunc(sym, tfn)
   204  	np := asNode(tfn.Type.Params().Field(0).Nname)
   205  	nh := asNode(tfn.Type.Params().Field(1).Nname)
   206  
   207  	// genhash is only called for types that have equality but
   208  	// cannot be handled by the standard algorithms,
   209  	// so t must be either an array or a struct.
   210  	switch t.Etype {
   211  	default:
   212  		Fatalf("genhash %v", t)
   213  
   214  	case types.TARRAY:
   215  		// An array of pure memory would be handled by the
   216  		// standard algorithm, so the element type must not be
   217  		// pure memory.
   218  		hashel := hashfor(t.Elem())
   219  
   220  		n := nod(ORANGE, nil, nod(ODEREF, np, nil))
   221  		ni := newname(lookup("i"))
   222  		ni.Type = types.Types[TINT]
   223  		n.List.Set1(ni)
   224  		n.SetColas(true)
   225  		colasdefn(n.List.Slice(), n)
   226  		ni = n.List.First()
   227  
   228  		// h = hashel(&p[i], h)
   229  		call := nod(OCALL, hashel, nil)
   230  
   231  		nx := nod(OINDEX, np, ni)
   232  		nx.SetBounded(true)
   233  		na := nod(OADDR, nx, nil)
   234  		call.List.Append(na)
   235  		call.List.Append(nh)
   236  		n.Nbody.Append(nod(OAS, nh, call))
   237  
   238  		fn.Nbody.Append(n)
   239  
   240  	case types.TSTRUCT:
   241  		// Walk the struct using memhash for runs of AMEM
   242  		// and calling specific hash functions for the others.
   243  		for i, fields := 0, t.FieldSlice(); i < len(fields); {
   244  			f := fields[i]
   245  
   246  			// Skip blank fields.
   247  			if f.Sym.IsBlank() {
   248  				i++
   249  				continue
   250  			}
   251  
   252  			// Hash non-memory fields with appropriate hash function.
   253  			if !IsRegularMemory(f.Type) {
   254  				hashel := hashfor(f.Type)
   255  				call := nod(OCALL, hashel, nil)
   256  				nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
   257  				na := nod(OADDR, nx, nil)
   258  				call.List.Append(na)
   259  				call.List.Append(nh)
   260  				fn.Nbody.Append(nod(OAS, nh, call))
   261  				i++
   262  				continue
   263  			}
   264  
   265  			// Otherwise, hash a maximal length run of raw memory.
   266  			size, next := memrun(t, i)
   267  
   268  			// h = hashel(&p.first, size, h)
   269  			hashel := hashmem(f.Type)
   270  			call := nod(OCALL, hashel, nil)
   271  			nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages?
   272  			na := nod(OADDR, nx, nil)
   273  			call.List.Append(na)
   274  			call.List.Append(nh)
   275  			call.List.Append(nodintconst(size))
   276  			fn.Nbody.Append(nod(OAS, nh, call))
   277  
   278  			i = next
   279  		}
   280  	}
   281  
   282  	r := nod(ORETURN, nil, nil)
   283  	r.List.Append(nh)
   284  	fn.Nbody.Append(r)
   285  
   286  	if Debug['r'] != 0 {
   287  		dumplist("genhash body", fn.Nbody)
   288  	}
   289  
   290  	funcbody()
   291  
   292  	fn.Func.SetDupok(true)
   293  	fn = typecheck(fn, ctxStmt)
   294  
   295  	Curfn = fn
   296  	typecheckslice(fn.Nbody.Slice(), ctxStmt)
   297  	Curfn = nil
   298  
   299  	if debug_dclstack != 0 {
   300  		testdclstack()
   301  	}
   302  
   303  	fn.Func.SetNilCheckDisabled(true)
   304  	funccompile(fn)
   305  }
   306  
   307  func hashfor(t *types.Type) *Node {
   308  	var sym *types.Sym
   309  
   310  	switch a, _ := algtype1(t); a {
   311  	case AMEM:
   312  		Fatalf("hashfor with AMEM type")
   313  	case AINTER:
   314  		sym = Runtimepkg.Lookup("interhash")
   315  	case ANILINTER:
   316  		sym = Runtimepkg.Lookup("nilinterhash")
   317  	case ASTRING:
   318  		sym = Runtimepkg.Lookup("strhash")
   319  	case AFLOAT32:
   320  		sym = Runtimepkg.Lookup("f32hash")
   321  	case AFLOAT64:
   322  		sym = Runtimepkg.Lookup("f64hash")
   323  	case ACPLX64:
   324  		sym = Runtimepkg.Lookup("c64hash")
   325  	case ACPLX128:
   326  		sym = Runtimepkg.Lookup("c128hash")
   327  	default:
   328  		sym = typesymprefix(".hash", t)
   329  	}
   330  
   331  	n := newname(sym)
   332  	n.SetClass(PFUNC)
   333  	n.Sym.SetFunc(true)
   334  	n.Type = functype(nil, []*Node{
   335  		anonfield(types.NewPtr(t)),
   336  		anonfield(types.Types[TUINTPTR]),
   337  	}, []*Node{
   338  		anonfield(types.Types[TUINTPTR]),
   339  	})
   340  	return n
   341  }
   342  
   343  // geneq generates a helper function to
   344  // check equality of two values of type t.
   345  func geneq(sym *types.Sym, t *types.Type) {
   346  	if Debug['r'] != 0 {
   347  		fmt.Printf("geneq %v %v\n", sym, t)
   348  	}
   349  
   350  	lineno = autogeneratedPos // less confusing than end of input
   351  	dclcontext = PEXTERN
   352  
   353  	// func sym(p, q *T) bool
   354  	tfn := nod(OTFUNC, nil, nil)
   355  	tfn.List.Set2(
   356  		namedfield("p", types.NewPtr(t)),
   357  		namedfield("q", types.NewPtr(t)),
   358  	)
   359  	tfn.Rlist.Set1(anonfield(types.Types[TBOOL]))
   360  
   361  	fn := dclfunc(sym, tfn)
   362  	np := asNode(tfn.Type.Params().Field(0).Nname)
   363  	nq := asNode(tfn.Type.Params().Field(1).Nname)
   364  
   365  	// geneq is only called for types that have equality but
   366  	// cannot be handled by the standard algorithms,
   367  	// so t must be either an array or a struct.
   368  	switch t.Etype {
   369  	default:
   370  		Fatalf("geneq %v", t)
   371  
   372  	case TARRAY:
   373  		// An array of pure memory would be handled by the
   374  		// standard memequal, so the element type must not be
   375  		// pure memory. Even if we unrolled the range loop,
   376  		// each iteration would be a function call, so don't bother
   377  		// unrolling.
   378  		nrange := nod(ORANGE, nil, nod(ODEREF, np, nil))
   379  
   380  		ni := newname(lookup("i"))
   381  		ni.Type = types.Types[TINT]
   382  		nrange.List.Set1(ni)
   383  		nrange.SetColas(true)
   384  		colasdefn(nrange.List.Slice(), nrange)
   385  		ni = nrange.List.First()
   386  
   387  		// if p[i] != q[i] { return false }
   388  		nx := nod(OINDEX, np, ni)
   389  
   390  		nx.SetBounded(true)
   391  		ny := nod(OINDEX, nq, ni)
   392  		ny.SetBounded(true)
   393  
   394  		nif := nod(OIF, nil, nil)
   395  		nif.Left = nod(ONE, nx, ny)
   396  		r := nod(ORETURN, nil, nil)
   397  		r.List.Append(nodbool(false))
   398  		nif.Nbody.Append(r)
   399  		nrange.Nbody.Append(nif)
   400  		fn.Nbody.Append(nrange)
   401  
   402  		// return true
   403  		ret := nod(ORETURN, nil, nil)
   404  		ret.List.Append(nodbool(true))
   405  		fn.Nbody.Append(ret)
   406  
   407  	case TSTRUCT:
   408  		var cond *Node
   409  		and := func(n *Node) {
   410  			if cond == nil {
   411  				cond = n
   412  				return
   413  			}
   414  			cond = nod(OANDAND, cond, n)
   415  		}
   416  
   417  		// Walk the struct using memequal for runs of AMEM
   418  		// and calling specific equality tests for the others.
   419  		for i, fields := 0, t.FieldSlice(); i < len(fields); {
   420  			f := fields[i]
   421  
   422  			// Skip blank-named fields.
   423  			if f.Sym.IsBlank() {
   424  				i++
   425  				continue
   426  			}
   427  
   428  			// Compare non-memory fields with field equality.
   429  			if !IsRegularMemory(f.Type) {
   430  				and(eqfield(np, nq, f.Sym))
   431  				i++
   432  				continue
   433  			}
   434  
   435  			// Find maximal length run of memory-only fields.
   436  			size, next := memrun(t, i)
   437  
   438  			// TODO(rsc): All the calls to newname are wrong for
   439  			// cross-package unexported fields.
   440  			if s := fields[i:next]; len(s) <= 2 {
   441  				// Two or fewer fields: use plain field equality.
   442  				for _, f := range s {
   443  					and(eqfield(np, nq, f.Sym))
   444  				}
   445  			} else {
   446  				// More than two fields: use memequal.
   447  				and(eqmem(np, nq, f.Sym, size))
   448  			}
   449  			i = next
   450  		}
   451  
   452  		if cond == nil {
   453  			cond = nodbool(true)
   454  		}
   455  
   456  		ret := nod(ORETURN, nil, nil)
   457  		ret.List.Append(cond)
   458  		fn.Nbody.Append(ret)
   459  	}
   460  
   461  	if Debug['r'] != 0 {
   462  		dumplist("geneq body", fn.Nbody)
   463  	}
   464  
   465  	funcbody()
   466  
   467  	fn.Func.SetDupok(true)
   468  	fn = typecheck(fn, ctxStmt)
   469  
   470  	Curfn = fn
   471  	typecheckslice(fn.Nbody.Slice(), ctxStmt)
   472  	Curfn = nil
   473  
   474  	if debug_dclstack != 0 {
   475  		testdclstack()
   476  	}
   477  
   478  	// Disable checknils while compiling this code.
   479  	// We are comparing a struct or an array,
   480  	// neither of which can be nil, and our comparisons
   481  	// are shallow.
   482  	fn.Func.SetNilCheckDisabled(true)
   483  	funccompile(fn)
   484  }
   485  
   486  // eqfield returns the node
   487  // 	p.field == q.field
   488  func eqfield(p *Node, q *Node, field *types.Sym) *Node {
   489  	nx := nodSym(OXDOT, p, field)
   490  	ny := nodSym(OXDOT, q, field)
   491  	ne := nod(OEQ, nx, ny)
   492  	return ne
   493  }
   494  
   495  // eqmem returns the node
   496  // 	memequal(&p.field, &q.field [, size])
   497  func eqmem(p *Node, q *Node, field *types.Sym, size int64) *Node {
   498  	nx := nod(OADDR, nodSym(OXDOT, p, field), nil)
   499  	ny := nod(OADDR, nodSym(OXDOT, q, field), nil)
   500  	nx = typecheck(nx, ctxExpr)
   501  	ny = typecheck(ny, ctxExpr)
   502  
   503  	fn, needsize := eqmemfunc(size, nx.Type.Elem())
   504  	call := nod(OCALL, fn, nil)
   505  	call.List.Append(nx)
   506  	call.List.Append(ny)
   507  	if needsize {
   508  		call.List.Append(nodintconst(size))
   509  	}
   510  
   511  	return call
   512  }
   513  
   514  func eqmemfunc(size int64, t *types.Type) (fn *Node, needsize bool) {
   515  	switch size {
   516  	default:
   517  		fn = syslook("memequal")
   518  		needsize = true
   519  	case 1, 2, 4, 8, 16:
   520  		buf := fmt.Sprintf("memequal%d", int(size)*8)
   521  		fn = syslook(buf)
   522  	}
   523  
   524  	fn = substArgTypes(fn, t, t)
   525  	return fn, needsize
   526  }
   527  
   528  // memrun finds runs of struct fields for which memory-only algs are appropriate.
   529  // t is the parent struct type, and start is the field index at which to start the run.
   530  // size is the length in bytes of the memory included in the run.
   531  // next is the index just after the end of the memory run.
   532  func memrun(t *types.Type, start int) (size int64, next int) {
   533  	next = start
   534  	for {
   535  		next++
   536  		if next == t.NumFields() {
   537  			break
   538  		}
   539  		// Stop run after a padded field.
   540  		if ispaddedfield(t, next-1) {
   541  			break
   542  		}
   543  		// Also, stop before a blank or non-memory field.
   544  		if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) {
   545  			break
   546  		}
   547  	}
   548  	return t.Field(next-1).End() - t.Field(start).Offset, next
   549  }
   550  
   551  // ispaddedfield reports whether the i'th field of struct type t is followed
   552  // by padding.
   553  func ispaddedfield(t *types.Type, i int) bool {
   554  	if !t.IsStruct() {
   555  		Fatalf("ispaddedfield called non-struct %v", t)
   556  	}
   557  	end := t.Width
   558  	if i+1 < t.NumFields() {
   559  		end = t.Field(i + 1).Offset
   560  	}
   561  	return t.Field(i).End() != end
   562  }