github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/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 && !fields[0].Sym.IsBlank() { 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 || f.Sym.IsBlank() || 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() 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 f.Sym.IsBlank() { 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() 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 fn.Func.SetNilCheckDisabled(true) 315 funccompile(fn) 316 317 safemode = old_safemode 318 } 319 320 func hashfor(t *types.Type) *Node { 321 var sym *types.Sym 322 323 switch a, _ := algtype1(t); a { 324 case AMEM: 325 Fatalf("hashfor with AMEM type") 326 case AINTER: 327 sym = Runtimepkg.Lookup("interhash") 328 case ANILINTER: 329 sym = Runtimepkg.Lookup("nilinterhash") 330 case ASTRING: 331 sym = Runtimepkg.Lookup("strhash") 332 case AFLOAT32: 333 sym = Runtimepkg.Lookup("f32hash") 334 case AFLOAT64: 335 sym = Runtimepkg.Lookup("f64hash") 336 case ACPLX64: 337 sym = Runtimepkg.Lookup("c64hash") 338 case ACPLX128: 339 sym = Runtimepkg.Lookup("c128hash") 340 default: 341 sym = typesymprefix(".hash", t) 342 } 343 344 n := newname(sym) 345 n.SetClass(PFUNC) 346 n.Type = functype(nil, []*Node{ 347 anonfield(types.NewPtr(t)), 348 anonfield(types.Types[TUINTPTR]), 349 }, []*Node{ 350 anonfield(types.Types[TUINTPTR]), 351 }) 352 return n 353 } 354 355 // geneq generates a helper function to 356 // check equality of two values of type t. 357 func geneq(sym *types.Sym, t *types.Type) { 358 if Debug['r'] != 0 { 359 fmt.Printf("geneq %v %v\n", sym, t) 360 } 361 362 lineno = autogeneratedPos // less confusing than end of input 363 dclcontext = PEXTERN 364 types.Markdcl() 365 366 // func sym(p, q *T) bool 367 tfn := nod(OTFUNC, nil, nil) 368 n := namedfield("p", types.NewPtr(t)) 369 tfn.List.Append(n) 370 np := n.Left 371 n = namedfield("q", types.NewPtr(t)) 372 tfn.List.Append(n) 373 nq := n.Left 374 n = anonfield(types.Types[TBOOL]) 375 tfn.Rlist.Append(n) 376 377 fn := dclfunc(sym, tfn) 378 379 // geneq is only called for types that have equality but 380 // cannot be handled by the standard algorithms, 381 // so t must be either an array or a struct. 382 switch t.Etype { 383 default: 384 Fatalf("geneq %v", t) 385 386 case TARRAY: 387 // An array of pure memory would be handled by the 388 // standard memequal, so the element type must not be 389 // pure memory. Even if we unrolled the range loop, 390 // each iteration would be a function call, so don't bother 391 // unrolling. 392 nrange := nod(ORANGE, nil, nod(OIND, np, nil)) 393 394 ni := newname(lookup("i")) 395 ni.Type = types.Types[TINT] 396 nrange.List.Set1(ni) 397 nrange.SetColas(true) 398 colasdefn(nrange.List.Slice(), nrange) 399 ni = nrange.List.First() 400 401 // if p[i] != q[i] { return false } 402 nx := nod(OINDEX, np, ni) 403 404 nx.SetBounded(true) 405 ny := nod(OINDEX, nq, ni) 406 ny.SetBounded(true) 407 408 nif := nod(OIF, nil, nil) 409 nif.Left = nod(ONE, nx, ny) 410 r := nod(ORETURN, nil, nil) 411 r.List.Append(nodbool(false)) 412 nif.Nbody.Append(r) 413 nrange.Nbody.Append(nif) 414 fn.Nbody.Append(nrange) 415 416 // return true 417 ret := nod(ORETURN, nil, nil) 418 ret.List.Append(nodbool(true)) 419 fn.Nbody.Append(ret) 420 421 case TSTRUCT: 422 var cond *Node 423 and := func(n *Node) { 424 if cond == nil { 425 cond = n 426 return 427 } 428 cond = nod(OANDAND, cond, n) 429 } 430 431 // Walk the struct using memequal for runs of AMEM 432 // and calling specific equality tests for the others. 433 for i, fields := 0, t.FieldSlice(); i < len(fields); { 434 f := fields[i] 435 436 // Skip blank-named fields. 437 if f.Sym.IsBlank() { 438 i++ 439 continue 440 } 441 442 // Compare non-memory fields with field equality. 443 if !IsRegularMemory(f.Type) { 444 and(eqfield(np, nq, f.Sym)) 445 i++ 446 continue 447 } 448 449 // Find maximal length run of memory-only fields. 450 size, next := memrun(t, i) 451 452 // TODO(rsc): All the calls to newname are wrong for 453 // cross-package unexported fields. 454 if s := fields[i:next]; len(s) <= 2 { 455 // Two or fewer fields: use plain field equality. 456 for _, f := range s { 457 and(eqfield(np, nq, f.Sym)) 458 } 459 } else { 460 // More than two fields: use memequal. 461 and(eqmem(np, nq, f.Sym, size)) 462 } 463 i = next 464 } 465 466 if cond == nil { 467 cond = nodbool(true) 468 } 469 470 ret := nod(ORETURN, nil, nil) 471 ret.List.Append(cond) 472 fn.Nbody.Append(ret) 473 } 474 475 if Debug['r'] != 0 { 476 dumplist("geneq body", fn.Nbody) 477 } 478 479 funcbody() 480 Curfn = fn 481 fn.Func.SetDupok(true) 482 fn = typecheck(fn, Etop) 483 typecheckslice(fn.Nbody.Slice(), Etop) 484 Curfn = nil 485 types.Popdcl() 486 if debug_dclstack != 0 { 487 testdclstack() 488 } 489 490 // Disable safemode while compiling this code: the code we 491 // generate internally can refer to unsafe.Pointer. 492 // In this case it can happen if we need to generate an == 493 // for a struct containing a reflect.Value, which itself has 494 // an unexported field of type unsafe.Pointer. 495 old_safemode := safemode 496 safemode = false 497 498 // Disable checknils while compiling this code. 499 // We are comparing a struct or an array, 500 // neither of which can be nil, and our comparisons 501 // are shallow. 502 fn.Func.SetNilCheckDisabled(true) 503 funccompile(fn) 504 505 safemode = old_safemode 506 } 507 508 // eqfield returns the node 509 // p.field == q.field 510 func eqfield(p *Node, q *Node, field *types.Sym) *Node { 511 nx := nodSym(OXDOT, p, field) 512 ny := nodSym(OXDOT, q, field) 513 ne := nod(OEQ, nx, ny) 514 return ne 515 } 516 517 // eqmem returns the node 518 // memequal(&p.field, &q.field [, size]) 519 func eqmem(p *Node, q *Node, field *types.Sym, size int64) *Node { 520 nx := nod(OADDR, nodSym(OXDOT, p, field), nil) 521 nx.Etype = 1 // does not escape 522 ny := nod(OADDR, nodSym(OXDOT, q, field), nil) 523 ny.Etype = 1 // does not escape 524 nx = typecheck(nx, Erv) 525 ny = typecheck(ny, Erv) 526 527 fn, needsize := eqmemfunc(size, nx.Type.Elem()) 528 call := nod(OCALL, fn, nil) 529 call.List.Append(nx) 530 call.List.Append(ny) 531 if needsize { 532 call.List.Append(nodintconst(size)) 533 } 534 535 return call 536 } 537 538 func eqmemfunc(size int64, t *types.Type) (fn *Node, needsize bool) { 539 switch size { 540 default: 541 fn = syslook("memequal") 542 needsize = true 543 case 1, 2, 4, 8, 16: 544 buf := fmt.Sprintf("memequal%d", int(size)*8) 545 fn = syslook(buf) 546 } 547 548 fn = substArgTypes(fn, t, t) 549 return fn, needsize 550 } 551 552 // memrun finds runs of struct fields for which memory-only algs are appropriate. 553 // t is the parent struct type, and start is the field index at which to start the run. 554 // size is the length in bytes of the memory included in the run. 555 // next is the index just after the end of the memory run. 556 func memrun(t *types.Type, start int) (size int64, next int) { 557 next = start 558 for { 559 next++ 560 if next == t.NumFields() { 561 break 562 } 563 // Stop run after a padded field. 564 if ispaddedfield(t, next-1) { 565 break 566 } 567 // Also, stop before a blank or non-memory field. 568 if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) { 569 break 570 } 571 } 572 return t.Field(next-1).End() - t.Field(start).Offset, next 573 } 574 575 // ispaddedfield reports whether the i'th field of struct type t is followed 576 // by padding. 577 func ispaddedfield(t *types.Type, i int) bool { 578 if !t.IsStruct() { 579 Fatalf("ispaddedfield called non-struct %v", t) 580 } 581 end := t.Width 582 if i+1 < t.NumFields() { 583 end = t.Field(i + 1).Offset 584 } 585 return t.Field(i).End() != end 586 }