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 }