github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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 = makePos(nil, 1, 0) // 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 = makePos(nil, 1, 0) // 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 }