github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 "github.com/gagliardetto/golang-go/cmd/compile/internal/types" 9 "github.com/gagliardetto/golang-go/cmd/internal/obj" 10 "fmt" 11 ) 12 13 // AlgKind describes the kind of algorithms used for comparing and 14 // hashing a Type. 15 type AlgKind int 16 17 const ( 18 // These values are known by runtime. 19 ANOEQ AlgKind = iota 20 AMEM0 21 AMEM8 22 AMEM16 23 AMEM32 24 AMEM64 25 AMEM128 26 ASTRING 27 AINTER 28 ANILINTER 29 AFLOAT32 30 AFLOAT64 31 ACPLX64 32 ACPLX128 33 34 // Type can be compared/hashed as regular memory. 35 AMEM AlgKind = 100 36 37 // Type needs special comparison/hashing functions. 38 ASPECIAL AlgKind = -1 39 ) 40 41 // IsComparable reports whether t is a comparable type. 42 func IsComparable(t *types.Type) bool { 43 a, _ := algtype1(t) 44 return a != ANOEQ 45 } 46 47 // IsRegularMemory reports whether t can be compared/hashed as regular memory. 48 func IsRegularMemory(t *types.Type) bool { 49 a, _ := algtype1(t) 50 return a == AMEM 51 } 52 53 // IncomparableField returns an incomparable Field of struct Type t, if any. 54 func IncomparableField(t *types.Type) *types.Field { 55 for _, f := range t.FieldSlice() { 56 if !IsComparable(f.Type) { 57 return f 58 } 59 } 60 return nil 61 } 62 63 // algtype is like algtype1, except it returns the fixed-width AMEMxx variants 64 // instead of the general AMEM kind when possible. 65 func algtype(t *types.Type) AlgKind { 66 a, _ := algtype1(t) 67 if a == AMEM { 68 switch t.Width { 69 case 0: 70 return AMEM0 71 case 1: 72 return AMEM8 73 case 2: 74 return AMEM16 75 case 4: 76 return AMEM32 77 case 8: 78 return AMEM64 79 case 16: 80 return AMEM128 81 } 82 } 83 84 return a 85 } 86 87 // algtype1 returns the AlgKind used for comparing and hashing Type t. 88 // If it returns ANOEQ, it also returns the component type of t that 89 // makes it incomparable. 90 func algtype1(t *types.Type) (AlgKind, *types.Type) { 91 if t.Broke() { 92 return AMEM, nil 93 } 94 if t.Noalg() { 95 return ANOEQ, t 96 } 97 98 switch t.Etype { 99 case TANY, TFORW: 100 // will be defined later. 101 return ANOEQ, t 102 103 case TINT8, TUINT8, TINT16, TUINT16, 104 TINT32, TUINT32, TINT64, TUINT64, 105 TINT, TUINT, TUINTPTR, 106 TBOOL, TPTR, 107 TCHAN, TUNSAFEPTR: 108 return AMEM, nil 109 110 case TFUNC, TMAP: 111 return ANOEQ, t 112 113 case TFLOAT32: 114 return AFLOAT32, nil 115 116 case TFLOAT64: 117 return AFLOAT64, nil 118 119 case TCOMPLEX64: 120 return ACPLX64, nil 121 122 case TCOMPLEX128: 123 return ACPLX128, nil 124 125 case TSTRING: 126 return ASTRING, nil 127 128 case TINTER: 129 if t.IsEmptyInterface() { 130 return ANILINTER, nil 131 } 132 return AINTER, nil 133 134 case TSLICE: 135 return ANOEQ, t 136 137 case TARRAY: 138 a, bad := algtype1(t.Elem()) 139 switch a { 140 case AMEM: 141 return AMEM, nil 142 case ANOEQ: 143 return ANOEQ, bad 144 } 145 146 switch t.NumElem() { 147 case 0: 148 // We checked above that the element type is comparable. 149 return AMEM, nil 150 case 1: 151 // Single-element array is same as its lone element. 152 return a, nil 153 } 154 155 return ASPECIAL, nil 156 157 case TSTRUCT: 158 fields := t.FieldSlice() 159 160 // One-field struct is same as that one field alone. 161 if len(fields) == 1 && !fields[0].Sym.IsBlank() { 162 return algtype1(fields[0].Type) 163 } 164 165 ret := AMEM 166 for i, f := range fields { 167 // All fields must be comparable. 168 a, bad := algtype1(f.Type) 169 if a == ANOEQ { 170 return ANOEQ, bad 171 } 172 173 // Blank fields, padded fields, fields with non-memory 174 // equality need special compare. 175 if a != AMEM || f.Sym.IsBlank() || ispaddedfield(t, i) { 176 ret = ASPECIAL 177 } 178 } 179 180 return ret, nil 181 } 182 183 Fatalf("algtype1: unexpected type %v", t) 184 return 0, nil 185 } 186 187 // genhash returns a symbol which is the closure used to compute 188 // the hash of a value of type t. 189 // Note: the generated function must match runtime.typehash exactly. 190 func genhash(t *types.Type) *obj.LSym { 191 switch algtype(t) { 192 default: 193 // genhash is only called for types that have equality 194 Fatalf("genhash %v", t) 195 case AMEM0: 196 return sysClosure("memhash0") 197 case AMEM8: 198 return sysClosure("memhash8") 199 case AMEM16: 200 return sysClosure("memhash16") 201 case AMEM32: 202 return sysClosure("memhash32") 203 case AMEM64: 204 return sysClosure("memhash64") 205 case AMEM128: 206 return sysClosure("memhash128") 207 case ASTRING: 208 return sysClosure("strhash") 209 case AINTER: 210 return sysClosure("interhash") 211 case ANILINTER: 212 return sysClosure("nilinterhash") 213 case AFLOAT32: 214 return sysClosure("f32hash") 215 case AFLOAT64: 216 return sysClosure("f64hash") 217 case ACPLX64: 218 return sysClosure("c64hash") 219 case ACPLX128: 220 return sysClosure("c128hash") 221 case AMEM: 222 // For other sizes of plain memory, we build a closure 223 // that calls memhash_varlen. The size of the memory is 224 // encoded in the first slot of the closure. 225 closure := typeLookup(fmt.Sprintf(".hashfunc%d", t.Width)).Linksym() 226 if len(closure.P) > 0 { // already generated 227 return closure 228 } 229 if memhashvarlen == nil { 230 memhashvarlen = sysfunc("memhash_varlen") 231 } 232 ot := 0 233 ot = dsymptr(closure, ot, memhashvarlen, 0) 234 ot = duintptr(closure, ot, uint64(t.Width)) // size encoded in closure 235 ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA) 236 return closure 237 case ASPECIAL: 238 break 239 } 240 241 closure := typesymprefix(".hashfunc", t).Linksym() 242 if len(closure.P) > 0 { // already generated 243 return closure 244 } 245 246 // Generate hash functions for subtypes. 247 // There are cases where we might not use these hashes, 248 // but in that case they will get dead-code eliminated. 249 // (And the closure generated by genhash will also get 250 // dead-code eliminated, as we call the subtype hashers 251 // directly.) 252 switch t.Etype { 253 case types.TARRAY: 254 genhash(t.Elem()) 255 case types.TSTRUCT: 256 for _, f := range t.FieldSlice() { 257 genhash(f.Type) 258 } 259 } 260 261 sym := typesymprefix(".hash", t) 262 if Debug['r'] != 0 { 263 fmt.Printf("genhash %v %v %v\n", closure, sym, t) 264 } 265 266 lineno = autogeneratedPos // less confusing than end of input 267 dclcontext = PEXTERN 268 269 // func sym(p *T, h uintptr) uintptr 270 tfn := nod(OTFUNC, nil, nil) 271 tfn.List.Set2( 272 namedfield("p", types.NewPtr(t)), 273 namedfield("h", types.Types[TUINTPTR]), 274 ) 275 tfn.Rlist.Set1(anonfield(types.Types[TUINTPTR])) 276 277 fn := dclfunc(sym, tfn) 278 np := asNode(tfn.Type.Params().Field(0).Nname) 279 nh := asNode(tfn.Type.Params().Field(1).Nname) 280 281 switch t.Etype { 282 case types.TARRAY: 283 // An array of pure memory would be handled by the 284 // standard algorithm, so the element type must not be 285 // pure memory. 286 hashel := hashfor(t.Elem()) 287 288 n := nod(ORANGE, nil, nod(ODEREF, np, nil)) 289 ni := newname(lookup("i")) 290 ni.Type = types.Types[TINT] 291 n.List.Set1(ni) 292 n.SetColas(true) 293 colasdefn(n.List.Slice(), n) 294 ni = n.List.First() 295 296 // h = hashel(&p[i], h) 297 call := nod(OCALL, hashel, nil) 298 299 nx := nod(OINDEX, np, ni) 300 nx.SetBounded(true) 301 na := nod(OADDR, nx, nil) 302 call.List.Append(na) 303 call.List.Append(nh) 304 n.Nbody.Append(nod(OAS, nh, call)) 305 306 fn.Nbody.Append(n) 307 308 case types.TSTRUCT: 309 // Walk the struct using memhash for runs of AMEM 310 // and calling specific hash functions for the others. 311 for i, fields := 0, t.FieldSlice(); i < len(fields); { 312 f := fields[i] 313 314 // Skip blank fields. 315 if f.Sym.IsBlank() { 316 i++ 317 continue 318 } 319 320 // Hash non-memory fields with appropriate hash function. 321 if !IsRegularMemory(f.Type) { 322 hashel := hashfor(f.Type) 323 call := nod(OCALL, hashel, nil) 324 nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages? 325 na := nod(OADDR, nx, nil) 326 call.List.Append(na) 327 call.List.Append(nh) 328 fn.Nbody.Append(nod(OAS, nh, call)) 329 i++ 330 continue 331 } 332 333 // Otherwise, hash a maximal length run of raw memory. 334 size, next := memrun(t, i) 335 336 // h = hashel(&p.first, size, h) 337 hashel := hashmem(f.Type) 338 call := nod(OCALL, hashel, nil) 339 nx := nodSym(OXDOT, np, f.Sym) // TODO: fields from other packages? 340 na := nod(OADDR, nx, nil) 341 call.List.Append(na) 342 call.List.Append(nh) 343 call.List.Append(nodintconst(size)) 344 fn.Nbody.Append(nod(OAS, nh, call)) 345 346 i = next 347 } 348 } 349 350 r := nod(ORETURN, nil, nil) 351 r.List.Append(nh) 352 fn.Nbody.Append(r) 353 354 if Debug['r'] != 0 { 355 dumplist("genhash body", fn.Nbody) 356 } 357 358 funcbody() 359 360 fn.Func.SetDupok(true) 361 fn = typecheck(fn, ctxStmt) 362 363 Curfn = fn 364 typecheckslice(fn.Nbody.Slice(), ctxStmt) 365 Curfn = nil 366 367 if debug_dclstack != 0 { 368 testdclstack() 369 } 370 371 fn.Func.SetNilCheckDisabled(true) 372 funccompile(fn) 373 374 // Build closure. It doesn't close over any variables, so 375 // it contains just the function pointer. 376 dsymptr(closure, 0, sym.Linksym(), 0) 377 ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA) 378 379 return closure 380 } 381 382 func hashfor(t *types.Type) *Node { 383 var sym *types.Sym 384 385 switch a, _ := algtype1(t); a { 386 case AMEM: 387 Fatalf("hashfor with AMEM type") 388 case AINTER: 389 sym = Runtimepkg.Lookup("interhash") 390 case ANILINTER: 391 sym = Runtimepkg.Lookup("nilinterhash") 392 case ASTRING: 393 sym = Runtimepkg.Lookup("strhash") 394 case AFLOAT32: 395 sym = Runtimepkg.Lookup("f32hash") 396 case AFLOAT64: 397 sym = Runtimepkg.Lookup("f64hash") 398 case ACPLX64: 399 sym = Runtimepkg.Lookup("c64hash") 400 case ACPLX128: 401 sym = Runtimepkg.Lookup("c128hash") 402 default: 403 // Note: the caller of hashfor ensured that this symbol 404 // exists and has a body by calling genhash for t. 405 sym = typesymprefix(".hash", t) 406 } 407 408 n := newname(sym) 409 n.SetClass(PFUNC) 410 n.Sym.SetFunc(true) 411 n.Type = functype(nil, []*Node{ 412 anonfield(types.NewPtr(t)), 413 anonfield(types.Types[TUINTPTR]), 414 }, []*Node{ 415 anonfield(types.Types[TUINTPTR]), 416 }) 417 return n 418 } 419 420 // sysClosure returns a closure which will call the 421 // given runtime function (with no closed-over variables). 422 func sysClosure(name string) *obj.LSym { 423 s := sysvar(name + "·f") 424 if len(s.P) == 0 { 425 f := sysfunc(name) 426 dsymptr(s, 0, f, 0) 427 ggloblsym(s, int32(Widthptr), obj.DUPOK|obj.RODATA) 428 } 429 return s 430 } 431 432 // geneq returns a symbol which is the closure used to compute 433 // equality for two objects of type t. 434 func geneq(t *types.Type) *obj.LSym { 435 switch algtype(t) { 436 case ANOEQ: 437 // The runtime will panic if it tries to compare 438 // a type with a nil equality function. 439 return nil 440 case AMEM0: 441 return sysClosure("memequal0") 442 case AMEM8: 443 return sysClosure("memequal8") 444 case AMEM16: 445 return sysClosure("memequal16") 446 case AMEM32: 447 return sysClosure("memequal32") 448 case AMEM64: 449 return sysClosure("memequal64") 450 case AMEM128: 451 return sysClosure("memequal128") 452 case ASTRING: 453 return sysClosure("strequal") 454 case AINTER: 455 return sysClosure("interequal") 456 case ANILINTER: 457 return sysClosure("nilinterequal") 458 case AFLOAT32: 459 return sysClosure("f32equal") 460 case AFLOAT64: 461 return sysClosure("f64equal") 462 case ACPLX64: 463 return sysClosure("c64equal") 464 case ACPLX128: 465 return sysClosure("c128equal") 466 case AMEM: 467 // make equality closure. The size of the type 468 // is encoded in the closure. 469 closure := typeLookup(fmt.Sprintf(".eqfunc%d", t.Width)).Linksym() 470 if len(closure.P) != 0 { 471 return closure 472 } 473 if memequalvarlen == nil { 474 memequalvarlen = sysvar("memequal_varlen") // asm func 475 } 476 ot := 0 477 ot = dsymptr(closure, ot, memequalvarlen, 0) 478 ot = duintptr(closure, ot, uint64(t.Width)) 479 ggloblsym(closure, int32(ot), obj.DUPOK|obj.RODATA) 480 return closure 481 case ASPECIAL: 482 break 483 } 484 485 closure := typesymprefix(".eqfunc", t).Linksym() 486 if len(closure.P) > 0 { // already generated 487 return closure 488 } 489 sym := typesymprefix(".eq", t) 490 if Debug['r'] != 0 { 491 fmt.Printf("geneq %v\n", t) 492 } 493 494 // Autogenerate code for equality of structs and arrays. 495 496 lineno = autogeneratedPos // less confusing than end of input 497 dclcontext = PEXTERN 498 499 // func sym(p, q *T) bool 500 tfn := nod(OTFUNC, nil, nil) 501 tfn.List.Set2( 502 namedfield("p", types.NewPtr(t)), 503 namedfield("q", types.NewPtr(t)), 504 ) 505 tfn.Rlist.Set1(anonfield(types.Types[TBOOL])) 506 507 fn := dclfunc(sym, tfn) 508 np := asNode(tfn.Type.Params().Field(0).Nname) 509 nq := asNode(tfn.Type.Params().Field(1).Nname) 510 511 // We reach here only for types that have equality but 512 // cannot be handled by the standard algorithms, 513 // so t must be either an array or a struct. 514 switch t.Etype { 515 default: 516 Fatalf("geneq %v", t) 517 518 case TARRAY: 519 // An array of pure memory would be handled by the 520 // standard memequal, so the element type must not be 521 // pure memory. Even if we unrolled the range loop, 522 // each iteration would be a function call, so don't bother 523 // unrolling. 524 nrange := nod(ORANGE, nil, nod(ODEREF, np, nil)) 525 526 ni := newname(lookup("i")) 527 ni.Type = types.Types[TINT] 528 nrange.List.Set1(ni) 529 nrange.SetColas(true) 530 colasdefn(nrange.List.Slice(), nrange) 531 ni = nrange.List.First() 532 533 // if p[i] != q[i] { return false } 534 nx := nod(OINDEX, np, ni) 535 536 nx.SetBounded(true) 537 ny := nod(OINDEX, nq, ni) 538 ny.SetBounded(true) 539 540 nif := nod(OIF, nil, nil) 541 nif.Left = nod(ONE, nx, ny) 542 r := nod(ORETURN, nil, nil) 543 r.List.Append(nodbool(false)) 544 nif.Nbody.Append(r) 545 nrange.Nbody.Append(nif) 546 fn.Nbody.Append(nrange) 547 548 // return true 549 ret := nod(ORETURN, nil, nil) 550 ret.List.Append(nodbool(true)) 551 fn.Nbody.Append(ret) 552 553 case TSTRUCT: 554 var cond *Node 555 and := func(n *Node) { 556 if cond == nil { 557 cond = n 558 return 559 } 560 cond = nod(OANDAND, cond, n) 561 } 562 563 // Walk the struct using memequal for runs of AMEM 564 // and calling specific equality tests for the others. 565 for i, fields := 0, t.FieldSlice(); i < len(fields); { 566 f := fields[i] 567 568 // Skip blank-named fields. 569 if f.Sym.IsBlank() { 570 i++ 571 continue 572 } 573 574 // Compare non-memory fields with field equality. 575 if !IsRegularMemory(f.Type) { 576 and(eqfield(np, nq, f.Sym)) 577 i++ 578 continue 579 } 580 581 // Find maximal length run of memory-only fields. 582 size, next := memrun(t, i) 583 584 // TODO(rsc): All the calls to newname are wrong for 585 // cross-package unexported fields. 586 if s := fields[i:next]; len(s) <= 2 { 587 // Two or fewer fields: use plain field equality. 588 for _, f := range s { 589 and(eqfield(np, nq, f.Sym)) 590 } 591 } else { 592 // More than two fields: use memequal. 593 and(eqmem(np, nq, f.Sym, size)) 594 } 595 i = next 596 } 597 598 if cond == nil { 599 cond = nodbool(true) 600 } 601 602 ret := nod(ORETURN, nil, nil) 603 ret.List.Append(cond) 604 fn.Nbody.Append(ret) 605 } 606 607 if Debug['r'] != 0 { 608 dumplist("geneq body", fn.Nbody) 609 } 610 611 funcbody() 612 613 fn.Func.SetDupok(true) 614 fn = typecheck(fn, ctxStmt) 615 616 Curfn = fn 617 typecheckslice(fn.Nbody.Slice(), ctxStmt) 618 Curfn = nil 619 620 if debug_dclstack != 0 { 621 testdclstack() 622 } 623 624 // Disable checknils while compiling this code. 625 // We are comparing a struct or an array, 626 // neither of which can be nil, and our comparisons 627 // are shallow. 628 fn.Func.SetNilCheckDisabled(true) 629 funccompile(fn) 630 631 // Generate a closure which points at the function we just generated. 632 dsymptr(closure, 0, sym.Linksym(), 0) 633 ggloblsym(closure, int32(Widthptr), obj.DUPOK|obj.RODATA) 634 return closure 635 } 636 637 // eqfield returns the node 638 // p.field == q.field 639 func eqfield(p *Node, q *Node, field *types.Sym) *Node { 640 nx := nodSym(OXDOT, p, field) 641 ny := nodSym(OXDOT, q, field) 642 ne := nod(OEQ, nx, ny) 643 return ne 644 } 645 646 // eqmem returns the node 647 // memequal(&p.field, &q.field [, size]) 648 func eqmem(p *Node, q *Node, field *types.Sym, size int64) *Node { 649 nx := nod(OADDR, nodSym(OXDOT, p, field), nil) 650 ny := nod(OADDR, nodSym(OXDOT, q, field), nil) 651 nx = typecheck(nx, ctxExpr) 652 ny = typecheck(ny, ctxExpr) 653 654 fn, needsize := eqmemfunc(size, nx.Type.Elem()) 655 call := nod(OCALL, fn, nil) 656 call.List.Append(nx) 657 call.List.Append(ny) 658 if needsize { 659 call.List.Append(nodintconst(size)) 660 } 661 662 return call 663 } 664 665 func eqmemfunc(size int64, t *types.Type) (fn *Node, needsize bool) { 666 switch size { 667 default: 668 fn = syslook("memequal") 669 needsize = true 670 case 1, 2, 4, 8, 16: 671 buf := fmt.Sprintf("memequal%d", int(size)*8) 672 fn = syslook(buf) 673 } 674 675 fn = substArgTypes(fn, t, t) 676 return fn, needsize 677 } 678 679 // memrun finds runs of struct fields for which memory-only algs are appropriate. 680 // t is the parent struct type, and start is the field index at which to start the run. 681 // size is the length in bytes of the memory included in the run. 682 // next is the index just after the end of the memory run. 683 func memrun(t *types.Type, start int) (size int64, next int) { 684 next = start 685 for { 686 next++ 687 if next == t.NumFields() { 688 break 689 } 690 // Stop run after a padded field. 691 if ispaddedfield(t, next-1) { 692 break 693 } 694 // Also, stop before a blank or non-memory field. 695 if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) { 696 break 697 } 698 } 699 return t.Field(next-1).End() - t.Field(start).Offset, next 700 } 701 702 // ispaddedfield reports whether the i'th field of struct type t is followed 703 // by padding. 704 func ispaddedfield(t *types.Type, i int) bool { 705 if !t.IsStruct() { 706 Fatalf("ispaddedfield called non-struct %v", t) 707 } 708 end := t.Width 709 if i+1 < t.NumFields() { 710 end = t.Field(i + 1).Offset 711 } 712 return t.Field(i).End() != end 713 }