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 }