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