github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/reflectdata/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 reflectdata 6 7 import ( 8 "fmt" 9 10 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 11 "github.com/bir3/gocompiler/src/cmd/compile/internal/compare" 12 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 13 "github.com/bir3/gocompiler/src/cmd/compile/internal/objw" 14 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 15 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 16 "github.com/bir3/gocompiler/src/cmd/internal/obj" 17 ) 18 19 // AlgType returns the fixed-width AMEMxx variants instead of the general 20 // AMEM kind when possible. 21 func AlgType(t *types.Type) types.AlgKind { 22 a, _ := types.AlgType(t) 23 if a == types.AMEM { 24 if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() { 25 // For example, we can't treat [2]int16 as an int32 if int32s require 26 // 4-byte alignment. See issue 46283. 27 return a 28 } 29 switch t.Size() { 30 case 0: 31 return types.AMEM0 32 case 1: 33 return types.AMEM8 34 case 2: 35 return types.AMEM16 36 case 4: 37 return types.AMEM32 38 case 8: 39 return types.AMEM64 40 case 16: 41 return types.AMEM128 42 } 43 } 44 45 return a 46 } 47 48 // genhash returns a symbol which is the closure used to compute 49 // the hash of a value of type t. 50 // Note: the generated function must match runtime.typehash exactly. 51 func genhash(t *types.Type) *obj.LSym { 52 switch AlgType(t) { 53 default: 54 // genhash is only called for types that have equality 55 base.Fatalf("genhash %v", t) 56 case types.AMEM0: 57 return sysClosure("memhash0") 58 case types.AMEM8: 59 return sysClosure("memhash8") 60 case types.AMEM16: 61 return sysClosure("memhash16") 62 case types.AMEM32: 63 return sysClosure("memhash32") 64 case types.AMEM64: 65 return sysClosure("memhash64") 66 case types.AMEM128: 67 return sysClosure("memhash128") 68 case types.ASTRING: 69 return sysClosure("strhash") 70 case types.AINTER: 71 return sysClosure("interhash") 72 case types.ANILINTER: 73 return sysClosure("nilinterhash") 74 case types.AFLOAT32: 75 return sysClosure("f32hash") 76 case types.AFLOAT64: 77 return sysClosure("f64hash") 78 case types.ACPLX64: 79 return sysClosure("c64hash") 80 case types.ACPLX128: 81 return sysClosure("c128hash") 82 case types.AMEM: 83 // For other sizes of plain memory, we build a closure 84 // that calls memhash_varlen. The size of the memory is 85 // encoded in the first slot of the closure. 86 closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Size())) 87 if len(closure.P) > 0 { // already generated 88 return closure 89 } 90 if memhashvarlen == nil { 91 memhashvarlen = typecheck.LookupRuntimeFunc("memhash_varlen") 92 } 93 ot := 0 94 ot = objw.SymPtr(closure, ot, memhashvarlen, 0) 95 ot = objw.Uintptr(closure, ot, uint64(t.Size())) // size encoded in closure 96 objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA) 97 return closure 98 case types.ASPECIAL: 99 break 100 } 101 102 closure := TypeLinksymPrefix(".hashfunc", t) 103 if len(closure.P) > 0 { // already generated 104 return closure 105 } 106 107 // Generate hash functions for subtypes. 108 // There are cases where we might not use these hashes, 109 // but in that case they will get dead-code eliminated. 110 // (And the closure generated by genhash will also get 111 // dead-code eliminated, as we call the subtype hashers 112 // directly.) 113 switch t.Kind() { 114 case types.TARRAY: 115 genhash(t.Elem()) 116 case types.TSTRUCT: 117 for _, f := range t.FieldSlice() { 118 genhash(f.Type) 119 } 120 } 121 122 sym := TypeSymPrefix(".hash", t) 123 if base.Flag.LowerR != 0 { 124 fmt.Printf("genhash %v %v %v\n", closure, sym, t) 125 } 126 127 base.Pos = base.AutogeneratedPos // less confusing than end of input 128 typecheck.DeclContext = ir.PEXTERN 129 130 // func sym(p *T, h uintptr) uintptr 131 args := []*ir.Field{ 132 ir.NewField(base.Pos, typecheck.Lookup("p"), types.NewPtr(t)), 133 ir.NewField(base.Pos, typecheck.Lookup("h"), types.Types[types.TUINTPTR]), 134 } 135 results := []*ir.Field{ir.NewField(base.Pos, nil, types.Types[types.TUINTPTR])} 136 137 fn := typecheck.DeclFunc(sym, nil, args, results) 138 np := ir.AsNode(fn.Type().Params().Field(0).Nname) 139 nh := ir.AsNode(fn.Type().Params().Field(1).Nname) 140 141 switch t.Kind() { 142 case types.TARRAY: 143 // An array of pure memory would be handled by the 144 // standard algorithm, so the element type must not be 145 // pure memory. 146 hashel := hashfor(t.Elem()) 147 148 // for i := 0; i < nelem; i++ 149 ni := typecheck.Temp(types.Types[types.TINT]) 150 init := ir.NewAssignStmt(base.Pos, ni, ir.NewInt(0)) 151 cond := ir.NewBinaryExpr(base.Pos, ir.OLT, ni, ir.NewInt(t.NumElem())) 152 post := ir.NewAssignStmt(base.Pos, ni, ir.NewBinaryExpr(base.Pos, ir.OADD, ni, ir.NewInt(1))) 153 loop := ir.NewForStmt(base.Pos, nil, cond, post, nil) 154 loop.PtrInit().Append(init) 155 156 // h = hashel(&p[i], h) 157 call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil) 158 159 nx := ir.NewIndexExpr(base.Pos, np, ni) 160 nx.SetBounded(true) 161 na := typecheck.NodAddr(nx) 162 call.Args.Append(na) 163 call.Args.Append(nh) 164 loop.Body.Append(ir.NewAssignStmt(base.Pos, nh, call)) 165 166 fn.Body.Append(loop) 167 168 case types.TSTRUCT: 169 // Walk the struct using memhash for runs of AMEM 170 // and calling specific hash functions for the others. 171 for i, fields := 0, t.FieldSlice(); i < len(fields); { 172 f := fields[i] 173 174 // Skip blank fields. 175 if f.Sym.IsBlank() { 176 i++ 177 continue 178 } 179 180 // Hash non-memory fields with appropriate hash function. 181 if !compare.IsRegularMemory(f.Type) { 182 hashel := hashfor(f.Type) 183 call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil) 184 nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages? 185 na := typecheck.NodAddr(nx) 186 call.Args.Append(na) 187 call.Args.Append(nh) 188 fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call)) 189 i++ 190 continue 191 } 192 193 // Otherwise, hash a maximal length run of raw memory. 194 size, next := compare.Memrun(t, i) 195 196 // h = hashel(&p.first, size, h) 197 hashel := hashmem(f.Type) 198 call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil) 199 nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages? 200 na := typecheck.NodAddr(nx) 201 call.Args.Append(na) 202 call.Args.Append(nh) 203 call.Args.Append(ir.NewInt(size)) 204 fn.Body.Append(ir.NewAssignStmt(base.Pos, nh, call)) 205 206 i = next 207 } 208 } 209 210 r := ir.NewReturnStmt(base.Pos, nil) 211 r.Results.Append(nh) 212 fn.Body.Append(r) 213 214 if base.Flag.LowerR != 0 { 215 ir.DumpList("genhash body", fn.Body) 216 } 217 218 typecheck.FinishFuncBody() 219 220 fn.SetDupok(true) 221 typecheck.Func(fn) 222 223 ir.CurFunc = fn 224 typecheck.Stmts(fn.Body) 225 ir.CurFunc = nil 226 227 if base.Debug.DclStack != 0 { 228 types.CheckDclstack() 229 } 230 231 fn.SetNilCheckDisabled(true) 232 typecheck.Target.Decls = append(typecheck.Target.Decls, fn) 233 234 // Build closure. It doesn't close over any variables, so 235 // it contains just the function pointer. 236 objw.SymPtr(closure, 0, fn.Linksym(), 0) 237 objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA) 238 239 return closure 240 } 241 242 func hashfor(t *types.Type) ir.Node { 243 var sym *types.Sym 244 245 switch a, _ := types.AlgType(t); a { 246 case types.AMEM: 247 base.Fatalf("hashfor with AMEM type") 248 case types.AINTER: 249 sym = ir.Pkgs.Runtime.Lookup("interhash") 250 case types.ANILINTER: 251 sym = ir.Pkgs.Runtime.Lookup("nilinterhash") 252 case types.ASTRING: 253 sym = ir.Pkgs.Runtime.Lookup("strhash") 254 case types.AFLOAT32: 255 sym = ir.Pkgs.Runtime.Lookup("f32hash") 256 case types.AFLOAT64: 257 sym = ir.Pkgs.Runtime.Lookup("f64hash") 258 case types.ACPLX64: 259 sym = ir.Pkgs.Runtime.Lookup("c64hash") 260 case types.ACPLX128: 261 sym = ir.Pkgs.Runtime.Lookup("c128hash") 262 default: 263 // Note: the caller of hashfor ensured that this symbol 264 // exists and has a body by calling genhash for t. 265 sym = TypeSymPrefix(".hash", t) 266 } 267 268 // TODO(austin): This creates an ir.Name with a nil Func. 269 n := typecheck.NewName(sym) 270 ir.MarkFunc(n) 271 n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{ 272 types.NewField(base.Pos, nil, types.NewPtr(t)), 273 types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), 274 }, []*types.Field{ 275 types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), 276 })) 277 return n 278 } 279 280 // sysClosure returns a closure which will call the 281 // given runtime function (with no closed-over variables). 282 func sysClosure(name string) *obj.LSym { 283 s := typecheck.LookupRuntimeVar(name + "·f") 284 if len(s.P) == 0 { 285 f := typecheck.LookupRuntimeFunc(name) 286 objw.SymPtr(s, 0, f, 0) 287 objw.Global(s, int32(types.PtrSize), obj.DUPOK|obj.RODATA) 288 } 289 return s 290 } 291 292 // geneq returns a symbol which is the closure used to compute 293 // equality for two objects of type t. 294 func geneq(t *types.Type) *obj.LSym { 295 switch AlgType(t) { 296 case types.ANOEQ: 297 // The runtime will panic if it tries to compare 298 // a type with a nil equality function. 299 return nil 300 case types.AMEM0: 301 return sysClosure("memequal0") 302 case types.AMEM8: 303 return sysClosure("memequal8") 304 case types.AMEM16: 305 return sysClosure("memequal16") 306 case types.AMEM32: 307 return sysClosure("memequal32") 308 case types.AMEM64: 309 return sysClosure("memequal64") 310 case types.AMEM128: 311 return sysClosure("memequal128") 312 case types.ASTRING: 313 return sysClosure("strequal") 314 case types.AINTER: 315 return sysClosure("interequal") 316 case types.ANILINTER: 317 return sysClosure("nilinterequal") 318 case types.AFLOAT32: 319 return sysClosure("f32equal") 320 case types.AFLOAT64: 321 return sysClosure("f64equal") 322 case types.ACPLX64: 323 return sysClosure("c64equal") 324 case types.ACPLX128: 325 return sysClosure("c128equal") 326 case types.AMEM: 327 // make equality closure. The size of the type 328 // is encoded in the closure. 329 closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Size())) 330 if len(closure.P) != 0 { 331 return closure 332 } 333 if memequalvarlen == nil { 334 memequalvarlen = typecheck.LookupRuntimeFunc("memequal_varlen") 335 } 336 ot := 0 337 ot = objw.SymPtr(closure, ot, memequalvarlen, 0) 338 ot = objw.Uintptr(closure, ot, uint64(t.Size())) 339 objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA) 340 return closure 341 case types.ASPECIAL: 342 break 343 } 344 345 closure := TypeLinksymPrefix(".eqfunc", t) 346 if len(closure.P) > 0 { // already generated 347 return closure 348 } 349 sym := TypeSymPrefix(".eq", t) 350 if base.Flag.LowerR != 0 { 351 fmt.Printf("geneq %v\n", t) 352 } 353 354 // Autogenerate code for equality of structs and arrays. 355 356 base.Pos = base.AutogeneratedPos // less confusing than end of input 357 typecheck.DeclContext = ir.PEXTERN 358 359 // func sym(p, q *T) bool 360 fn := typecheck.DeclFunc(sym, nil, 361 []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("p"), types.NewPtr(t)), ir.NewField(base.Pos, typecheck.Lookup("q"), types.NewPtr(t))}, 362 []*ir.Field{ir.NewField(base.Pos, typecheck.Lookup("r"), types.Types[types.TBOOL])}, 363 ) 364 np := ir.AsNode(fn.Type().Params().Field(0).Nname) 365 nq := ir.AsNode(fn.Type().Params().Field(1).Nname) 366 nr := ir.AsNode(fn.Type().Results().Field(0).Nname) 367 368 // Label to jump to if an equality test fails. 369 neq := typecheck.AutoLabel(".neq") 370 371 // We reach here only for types that have equality but 372 // cannot be handled by the standard algorithms, 373 // so t must be either an array or a struct. 374 switch t.Kind() { 375 default: 376 base.Fatalf("geneq %v", t) 377 378 case types.TARRAY: 379 nelem := t.NumElem() 380 381 // checkAll generates code to check the equality of all array elements. 382 // If unroll is greater than nelem, checkAll generates: 383 // 384 // if eq(p[0], q[0]) && eq(p[1], q[1]) && ... { 385 // } else { 386 // goto neq 387 // } 388 // 389 // And so on. 390 // 391 // Otherwise it generates: 392 // 393 // iterateTo := nelem/unroll*unroll 394 // for i := 0; i < iterateTo; i += unroll { 395 // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) { 396 // } else { 397 // goto neq 398 // } 399 // } 400 // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... { 401 // } else { 402 // goto neq 403 // } 404 // 405 checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) { 406 // checkIdx generates a node to check for equality at index i. 407 checkIdx := func(i ir.Node) ir.Node { 408 // pi := p[i] 409 pi := ir.NewIndexExpr(base.Pos, np, i) 410 pi.SetBounded(true) 411 pi.SetType(t.Elem()) 412 // qi := q[i] 413 qi := ir.NewIndexExpr(base.Pos, nq, i) 414 qi.SetBounded(true) 415 qi.SetType(t.Elem()) 416 return eq(pi, qi) 417 } 418 419 iterations := nelem / unroll 420 iterateTo := iterations * unroll 421 // If a loop is iterated only once, there shouldn't be any loop at all. 422 if iterations == 1 { 423 iterateTo = 0 424 } 425 426 if iterateTo > 0 { 427 // Generate an unrolled for loop. 428 // for i := 0; i < nelem/unroll*unroll; i += unroll 429 i := typecheck.Temp(types.Types[types.TINT]) 430 init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0)) 431 cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(iterateTo)) 432 loop := ir.NewForStmt(base.Pos, nil, cond, nil, nil) 433 loop.PtrInit().Append(init) 434 435 // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) { 436 // } else { 437 // goto neq 438 // } 439 for j := int64(0); j < unroll; j++ { 440 // if check {} else { goto neq } 441 nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil) 442 nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) 443 loop.Body.Append(nif) 444 post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1))) 445 loop.Body.Append(post) 446 } 447 448 fn.Body.Append(loop) 449 450 if nelem == iterateTo { 451 if last { 452 fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true))) 453 } 454 return 455 } 456 } 457 458 // Generate remaining checks, if nelem is not a multiple of unroll. 459 if last { 460 // Do last comparison in a different manner. 461 nelem-- 462 } 463 // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... { 464 // } else { 465 // goto neq 466 // } 467 for j := iterateTo; j < nelem; j++ { 468 // if check {} else { goto neq } 469 nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(j)), nil, nil) 470 nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) 471 fn.Body.Append(nif) 472 } 473 if last { 474 fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem)))) 475 } 476 } 477 478 switch t.Elem().Kind() { 479 case types.TSTRING: 480 // Do two loops. First, check that all the lengths match (cheap). 481 // Second, check that all the contents match (expensive). 482 checkAll(3, false, func(pi, qi ir.Node) ir.Node { 483 // Compare lengths. 484 eqlen, _ := compare.EqString(pi, qi) 485 return eqlen 486 }) 487 checkAll(1, true, func(pi, qi ir.Node) ir.Node { 488 // Compare contents. 489 _, eqmem := compare.EqString(pi, qi) 490 return eqmem 491 }) 492 case types.TFLOAT32, types.TFLOAT64: 493 checkAll(2, true, func(pi, qi ir.Node) ir.Node { 494 // p[i] == q[i] 495 return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi) 496 }) 497 // TODO: pick apart structs, do them piecemeal too 498 default: 499 checkAll(1, true, func(pi, qi ir.Node) ir.Node { 500 // p[i] == q[i] 501 return ir.NewBinaryExpr(base.Pos, ir.OEQ, pi, qi) 502 }) 503 } 504 505 case types.TSTRUCT: 506 flatConds := compare.EqStruct(t, np, nq) 507 if len(flatConds) == 0 { 508 fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true))) 509 } else { 510 for _, c := range flatConds[:len(flatConds)-1] { 511 // if cond {} else { goto neq } 512 n := ir.NewIfStmt(base.Pos, c, nil, nil) 513 n.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq)) 514 fn.Body.Append(n) 515 } 516 fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, flatConds[len(flatConds)-1])) 517 } 518 } 519 520 // ret: 521 // return 522 ret := typecheck.AutoLabel(".ret") 523 fn.Body.Append(ir.NewLabelStmt(base.Pos, ret)) 524 fn.Body.Append(ir.NewReturnStmt(base.Pos, nil)) 525 526 // neq: 527 // r = false 528 // return (or goto ret) 529 fn.Body.Append(ir.NewLabelStmt(base.Pos, neq)) 530 fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(false))) 531 if compare.EqCanPanic(t) || anyCall(fn) { 532 // Epilogue is large, so share it with the equal case. 533 fn.Body.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, ret)) 534 } else { 535 // Epilogue is small, so don't bother sharing. 536 fn.Body.Append(ir.NewReturnStmt(base.Pos, nil)) 537 } 538 // TODO(khr): the epilogue size detection condition above isn't perfect. 539 // We should really do a generic CL that shares epilogues across 540 // the board. See #24936. 541 542 if base.Flag.LowerR != 0 { 543 ir.DumpList("geneq body", fn.Body) 544 } 545 546 typecheck.FinishFuncBody() 547 548 fn.SetDupok(true) 549 typecheck.Func(fn) 550 551 ir.CurFunc = fn 552 typecheck.Stmts(fn.Body) 553 ir.CurFunc = nil 554 555 if base.Debug.DclStack != 0 { 556 types.CheckDclstack() 557 } 558 559 // Disable checknils while compiling this code. 560 // We are comparing a struct or an array, 561 // neither of which can be nil, and our comparisons 562 // are shallow. 563 fn.SetNilCheckDisabled(true) 564 typecheck.Target.Decls = append(typecheck.Target.Decls, fn) 565 566 // Generate a closure which points at the function we just generated. 567 objw.SymPtr(closure, 0, fn.Linksym(), 0) 568 objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA) 569 return closure 570 } 571 572 func anyCall(fn *ir.Func) bool { 573 return ir.Any(fn, func(n ir.Node) bool { 574 // TODO(rsc): No methods? 575 op := n.Op() 576 return op == ir.OCALL || op == ir.OCALLFUNC 577 }) 578 } 579 580 func hashmem(t *types.Type) ir.Node { 581 sym := ir.Pkgs.Runtime.Lookup("memhash") 582 583 // TODO(austin): This creates an ir.Name with a nil Func. 584 n := typecheck.NewName(sym) 585 ir.MarkFunc(n) 586 n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{ 587 types.NewField(base.Pos, nil, types.NewPtr(t)), 588 types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), 589 types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), 590 }, []*types.Field{ 591 types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), 592 })) 593 return n 594 }